explicitement cast parameters de type génériques à toute interface

La FAQ sur les génériques: Meilleures pratiques dit:

Le compilateur vous permettra de convertir explicitement les parameters de type génériques vers n’importe quelle interface, mais pas vers une classe:

interface ISomeInterface {...} class SomeClass {...} class MyClass { void SomeMethod(T t) { ISomeInterface obj1 = (ISomeInterface)t;//Comstacks SomeClass obj2 = (SomeClass)t; //Does not comstack } } 

Je vois une limitation raisonnable pour les classes et les interfaces, à moins que la classe / interface ne soit pas spécifiée comme type de contrainte.

Alors pourquoi un tel comportement, pourquoi est-il autorisé pour les interfaces?

Je pense que cela est dû au fait que la conversion vers SomeClass peut signifier un certain nombre de choses selon les conversions disponibles, alors que la ISomeInterface vers ISomeInterface ne peut être qu’une conversion de référence ou une conversion de boxe.

Options:

  • Cast pour objecter en premier:

     SomeClass obj2 = (SomeClass) (object) t; 
  • Utiliser as place:

     SomeClass obj2 = t as SomeClass; 

Évidemment, dans le second cas, vous devrez également effectuer un contrôle de nullité par la suite, au cas où t ne serait pas une SomeClass .

EDIT: le raisonnement à cet égard est donné à la section 6.2.7 de la spécification C # 4:

Les règles ci-dessus ne permettent pas une conversion explicite directe d’un paramètre de type non contraint en un type sans interface, ce qui peut paraître surprenant. La raison de cette règle est d’éviter toute confusion et de clarifier la sémantique de ces conversions. Par exemple, considérons la déclaration suivante:

 class X { public static long F(T t) { return (long)t; // Error } } 

Si la conversion explicite directe de t en int était autorisée, on pourrait facilement s’attendre à ce que X.F(7) renvoie 7L. Cependant, ce n’est pas le cas, car les conversions numériques standard ne sont sockets en compte que lorsque les types sont connus pour être numériques au moment de la liaison. Afin de clarifier la sémantique, l’exemple ci-dessus doit plutôt être écrit:

 class X { public static long F(T t) { return (long)(object)t; // Ok, but will only work when T is long } } 

Ce code va maintenant être compilé, mais l’exécution de X.F(7) lèvera alors une exception au moment de l’exécution, puisqu’un int en boîte ne peut pas être converti directement en long.

Dans le principe d’inheritance de C #, les interfaces peuvent être héritées plusieurs fois, mais une classe une seule fois. Comme l’inheritance des interfaces a une hiérarchie complexe, le framework .net n’a pas besoin de garantir au type générique T une interface spécifique au moment de la compilation. (EDIT) Au contraire, une classe pourrait être assurée d’une classe spécifique avec la déclaration d’une contrainte de type à la compilation en tant que code suivant.

 class MyClass where T : SomeClass { void SomeMethod(T t) { ISomeInterface obj1 = (ISomeInterface)t; SomeClass obj2 = (SomeClass)t; } }