28 votes

Je ne peux que lancer un délégué contravariant avec "as"

Je suis en train de jeter un contravariant délégué, mais pour une raison que je ne peut le faire qu'en utilisant le "comme" de l'opérateur.

interface MyInterface { }
delegate void MyFuncType<in InType>(InType input);

class MyClass<T> where T : MyInterface
{
    public void callDelegate(MyFuncType<MyInterface> func)
    {
        MyFuncType<T> castFunc1 = (MyFuncType <T>) func; //Error
        MyFuncType<T> castFunc2 = func as MyFuncType<T>; 
        MyFuncType<T> castFunc3 = func is MyFuncType<T> ? (MyFuncType<T>)func : (MyFuncType<T>)null; //Error
    }
}

castFunc2 fonctionne très bien mais castFunc1 et castFunc3 la cause de l'erreur:

Cannot convert type 'delegateCovariance.MyFuncType<myNamespace.MyInterface>' to myNamespace.MyFuncType<T>'

L' article MSDN sur l'opérateur déclare que castFunc2 et castFunc3 sont "équivalentes", donc je ne comprends pas comment un seul d'entre eux pourrait provoquer une erreur. Un autre morceau de ce qui est déroutant moi est que le changement de MyInterface à partir d'une interface à une classe de se débarrasser de l'erreur.

Quelqu'un peut-il m'aider à comprendre ce qui se passe ici? Merci!

15voto

Joshua Enfield Points 4288

Ajouter une contrainte telle que T doit être une classe.

class MyClass<T> where T: class, MyInterface

Cela donne le compilateur assez d'informations pour savoir que T est convertible. Vous n'avez pas besoin de cast explicite soit.

La Variance s'applique uniquement aux types de référence. T est autorisé à être un type de la valeur sans la contrainte qui rompt les compilateurs capacité de prouver que T est compatible pour la contravariance.

La raison de la deuxième déclaration de travaux est parce qu' as peut effectivement effectuer une nulle de conversion. Par exemple:

class SomeClass { }
interface SomeInterface { }
static void Main(string[] args)
{
   SomeClass foo = null;
   SomeInterface bar = foo as SomeInterface;
}

Foo n'est évidemment pas directement convertible pour SomeInterface, mais il réussit encore à cause d'un nul conversion peut encore avoir lieu. Votre référence MSDN peut être correct pour la plupart des scénarios, mais le code généré IL est très différent, ce qui signifie qu'ils sont fondamentalement différents à partir d'un point de vue technique.

4voto

Sergey Teplyakov Points 6556

Eric Lippert a donné une bonne explication de cette question dans ses messages récents: Un "est" opérateur de puzzle, première partie, Un "est" opérateur de puzzle, partie deux.

Principale raison de ce comportement est la suivante: "est" (ou "comme") les opérateurs ne sont pas les mêmes que d'un plâtre. "comme" opérateur peut entraîner la non-nulle de la suite des événements si correspondant cast serait illégal, et cela est particulièrement vrai lorsque nous sommes face à des arguments de type.

Fondamentalement, cast opérateur dans votre cas, signifie (comme Eric l'a dit) que "je sais que cette valeur est d'un type donné, même si le compilateur ne sait pas que, le compilateur doit permettre" ou "je sais que ce n'est pas une valeur d'un type donné; générer des fins spéciales, spécifiques à un type de code pour convertir une valeur d'un type, d'une valeur d'un type différent."

Plus tard, affaire porte sur la valeur de telles conversions double-conversion d'entier et nous ne pouvons l'ignorer ce sens dans le contexte actuel.

Et de type générique arguments ne sont pas logiques dans le premier contexte ni. Si vous avez affaire à un argument de type générique, pourquoi vous n'êtes pas à l'indiquer, le "contrat" de toute évidence, en utilisant l'argument de type générique?

Je ne suis pas 100% sûr de ce que vous êtes voulez atteindre, mais vous pouvez omettre type spécial de votre méthode et utiliser librement argument générique à la place:

class MyClass<T> where T : MyInterface
{
    public void callDelegate(Action<T> func)
    {
    }
}

class MyClass2
{
    public void callDelegate<T>(Action<T> func)
        where T : MyInterface
    {
    }
}

Sinon, vous devez utiliser as opérateur de vérifier la valeur null au lieu de vérification de type.

-1voto

Jake Points 82

Votre classe indique que T implémente MyInterface car MyInterface n'est pas un type d'instance. Par conséquent, MyFuncType<T> n'est pas garanti comme étant MyFuncType<MyInterface> . Ce pourrait être MyFuncType<SomeType> et SomeType : MyInterface , mais ce ne serait pas la même chose que SomeOtherType : MyInterface . Ça a du sens?

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