210 votes

Comment puis-je utiliser l'interface comme une contrainte de type générique C# ?

Existe-t-il un moyen d'obtenir la déclaration de fonction suivante ?

public bool Foo<T>() where T : interface;

c'est-à-dire où T est un type d'interface (similaire à where T : class et struct ).

Actuellement, je me suis contenté de :

public bool Foo<T>() where T : IBase;

Où IBase est définie comme une interface vide qui est héritée par toutes mes interfaces personnalisées... Pas idéal, mais cela devrait fonctionner... Pourquoi ne pouvez-vous pas définir qu'un type générique doit être une interface ?

Pour ce que ça vaut, je veux ça parce que Foo fait de la réflexion où il a besoin d'un type d'interface... Je pourrais le passer en tant que paramètre normal et faire les vérifications nécessaires dans la fonction elle-même, mais ceci semblait beaucoup plus sûr (et je suppose un peu plus performant, puisque toutes les vérifications sont faites au moment de la compilation).

5 votes

En fait, votre IBase dea est le meilleur que j'ai vu jusqu'à présent. Malheureusement, vous ne pouvez pas l'utiliser pour les interfaces que vous ne possédez pas. Tout ce que C# devrait faire, c'est que toutes les interfaces héritent de IOjbect, tout comme toutes les classes héritent de Object.

3 votes

Note : Il se trouve que c'est une idée assez courante. Les interfaces vides comme IBase - utilisés de cette manière - sont appelés interfaces de marquage . Ils permettent des comportements spéciaux pour les types "marqués".

0 votes

Autre note : Selon Directives pour la conception d'interfaces l'utilisation d'interfaces de marquage doit être évitée

159voto

Marc Gravell Points 482669

Le plus proche que vous puissiez faire (sauf pour votre approche de l'interface de base) est " where T : class ", qui signifie type de référence. Il n'existe pas de syntaxe pour signifier "toute interface".

Cette (" where T : class ") est utilisé, par exemple, dans WCF pour limiter les clients aux contrats de service (interfaces).

14 votes

Belle réponse, mais avez-vous une idée pourquoi cette syntaxe n'existe pas ? Il semblerait que ce soit une fonctionnalité intéressante.

2 votes

@StephenHolt : Je pense que les créateurs de .NET, en décidant des contraintes à autoriser, se sont concentrés sur celles qui permettraient aux classes et méthodes génériques de faire des choses avec les types génériques qu'elles ne pourraient pas faire autrement, plutôt que de les empêcher d'être utilisées de manière absurde. Cela étant dit, une interface contrainte sur T devrait permettre des comparaisons de référence entre T et tout autre type de référence, puisque les comparaisons de référence sont autorisées entre toute interface et presque tout autre type de référence, et qu'autoriser les comparaisons même dans ce cas ne poserait aucun problème.

1 votes

@supercat une autre application utile de cette contrainte hypothétique serait de créer en toute sécurité un proxy pour les instances du type. Pour les interfaces, la sécurité est garantie, tandis que pour les classes scellées, elle échouerait, tout comme pour les classes avec des méthodes non virtuelles.

131voto

Robert Points 1439

Je sais que c'est un peu tard mais pour ceux qui sont intéressés, vous pouvez utiliser un contrôle d'exécution.

typeof(T).IsInterface

12 votes

+1 pour être la seule réponse à le signaler. Je viens d'ajouter une réponse avec une approche pour améliorer les performances en vérifiant chaque type une seule fois plutôt que chaque fois que la méthode est appelée.

13 votes

L'idée générale des génériques en C# est d'avoir une sécurité au moment de la compilation. Ce que vous suggérez peut tout aussi bien être réalisé avec une méthode non générique Foo(Type type) .

0 votes

J'aime la vérification du temps d'exécution. Merci.

29voto

Mehrdad Afshari Points 204872

Non, en fait, si vous pensez class et struct moyenne class es et struct s, vous avez tort. class signifie tout type de référence (qui comprend également les interfaces) et struct signifie tout type de valeur (par exemple struct , enum ).

1 votes

N'est-ce pas là la définition de la différence entre une classe et une structure : que chaque classe est un type de référence (et vice versa) et idem pour les types de structure/valeur.

0 votes

Matthew : Les types de valeurs ne se limitent pas aux structs C#. Les Enums, par exemple, sont des types de valeur et correspondent à where T : struct contrainte.

0 votes

Il est bon de noter que les types d'interface utilisés dans les contraintes n'impliquent pas class Mais déclarer un emplacement de stockage d'un type d'interface revient à déclarer que l'emplacement de stockage est une référence de classe qui implémente ce type.

22voto

phoog Points 22667

Pour faire suite à la réponse de Robert, c'est encore plus tard, mais vous pouvez utiliser une classe d'aide statique pour que le runtime ne vérifie qu'une seule fois par type :

public bool Foo<T>() where T : class
{
    FooHelper<T>.Foo();
}

private static class FooHelper<TInterface> where TInterface : class
{
    static FooHelper()
    {
        if (!typeof(TInterface).IsInterface)
            throw // ... some exception
    }
    public static void Foo() { /*...*/ }
}

Je note également que votre solution "devrait fonctionner" ne fonctionne pas, en fait. Considérez :

public bool Foo<T>() where T : IBase;
public interface IBase { }
public interface IActual : IBase { string S { get; } }
public class Actual : IActual { public string S { get; set; } }

Maintenant, rien ne vous empêche d'appeler Foo ainsi :

Foo<Actual>();

Le site Actual après tout, la classe satisfait à l'exigence IBase contrainte.

0 votes

A static ne peut pas être public donc cela devrait donner une erreur de compilation. De plus, votre static contient une méthode d'instance, c'est également une erreur de compilation.

0 votes

Remerciements tardifs à nawfal pour avoir corrigé les erreurs notées par @JeppeStigNielsen

8voto

Pavel Minaev Points 60647

Vous ne pouvez pas le faire dans les versions publiées de C#, ni dans la future version 4.0 de C#. Ce n'est pas non plus une limitation de C# - il n'y a pas de contrainte d'"interface" dans le CLR lui-même.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X