c# generics .net-4.0 casting contravariance

c# - Solo puedo enviar un delegado contravariante con "como"



generics .net-4.0 (3)

Eric Lippert dio una gran explicación de este tema en sus publicaciones recientes: un rompecabezas del operador "es", primera parte , un rompecabezas del operador "es", segunda parte .

La lógica principal detrás de este comportamiento es la siguiente: los operadores "is" (o "as") no son lo mismo que un elenco. El operador "como" puede dar como resultado un evento de resultado no nulo si el lanzamiento correspondiente sería ilegal, y esto es especialmente cierto cuando se trata de argumentos de tipo.

Básicamente, el operador de cast en su caso significa (como dijo Eric) que "sé que este valor es del tipo dado , aunque el compilador no lo sepa, el compilador debería permitirlo" o "Sé que este valor no es del tipo dado ; genere un código específico de tipo específico para convertir un valor de un tipo a un valor de un tipo diferente ".

Más adelante, el caso trata las conversiones de valor, como la conversión de doble a entero, y podemos ignorar este significado en el contexto actual.

Y los argumentos de tipo genérico tampoco son lógicos en el primer contexto. Si está tratando con un argumento de tipo genérico, ¿por qué no está diciendo claramente este "contrato" mediante el uso de un argumento de tipo genérico?

No estoy 100% seguro de lo que desea lograr, pero puede omitir el tipo especial de su método y usar libremente el argumento genérico en su lugar:

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

De lo contrario, debe utilizar as operador con la verificación de nulo en lugar de verificación de tipo.

Estoy tratando de elegir un delegado contravariante, pero por alguna razón solo puedo hacerlo usando el operador "como".

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 funciona bien pero castFunc1 y castFunc3 causan el error:

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

El artículo de MSDN sobre el operador as indica que castFunc2 y castFunc3 son "equivalentes", por lo que no entiendo cómo solo uno de ellos podría causar un error. Otra parte de esto que me confunde es que cambiar MyInterface de una interfaz a una clase elimina el error.

¿Alguien puede ayudarme a entender lo que está pasando aquí? ¡Gracias!


Su clase dice que T implementa MyInterface porque MyInterface no es un tipo de instancia. Por lo tanto, no se garantiza que MyFuncType<T> sea MyFuncType<MyInterface> . Podría ser MyFuncType<SomeType> y SomeType : MyInterface , pero eso no sería lo mismo que SomeOtherType : MyInterface . ¿Tener sentido?


Agregue una restricción tal que T debe ser una clase.

class MyClass<T> where T: class, MyInterface

Esto le da al compilador suficiente información para saber que T es convertible. No necesitas el molde explícito tampoco.

La varianza solo se aplica a los tipos de referencia. Se permite que T sea un tipo de valor sin la restricción que rompe la capacidad del compilador de probar que T es compatible por contravarianza.

El motivo por el que funciona la segunda declaración es porque realmente puede realizar una conversión nula. Por ejemplo:

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

Es obvio que Foo no se puede convertir directamente en SomeInterface , pero aún tiene éxito porque aún puede producirse una conversión nula. Su referencia de MSDN puede ser correcta para la mayoría de los escenarios, pero el código IL generado es muy diferente, lo que significa que son fundamentalmente diferentes desde una perspectiva técnica.