Pregunta

Recientemente encontré un comportamiento muy sorprendente en c #. Tuve un método que toma IEnumerable < Object > como parámetro y pasé IEnumerable < string > pero no es posible. ¿Mientras que en c # todo puede ser transmitido al objeto que por qué esto no es posible? Es totalmente confuso para mí. Por favor, alguien me aclare sobre este tema.

¿Fue útil?

Solución

El término técnico para esto es que los genéricos son invariantes en C # 3.0 y anteriores. Desde C # 4.0 en adelante, el reparto funciona.

Lo que significa invariante es que no hay relación entre dos tipos genéricos solo porque sus tipos genéricos parámetros están relacionados (es decir, son sub o supertipos entre sí).

En su ejemplo, no hay una relación de escritura entre un IEnumerable < object > y un IEnumerable < string > , simplemente porque string es un subtipo de objeto. Solo se consideran dos tipos completamente no relacionados, como una cadena y un int (aún son subtipos de objeto, pero todo es)

Hay algunas soluciones alternativas y excepciones para este problema que has encontrado.

Primero, puede convertir cada cadena individualmente en un objeto. Si está utilizando .NET 3.0, puede hacerlo utilizando el método de extensión Cast < T > () . De lo contrario, puede usar un foreach y colocar el resultado en una nueva variable del tipo estático que desee.

En segundo lugar, las matrices son una excepción para el tipo de referencia, es decir, pasar un tipo de cadena [] a un método que acepte los tipos de objeto [] debería funcionar.

Otros consejos

Como han señalado otros, los tipos genéricos son invariantes. IEnumerable < T > podría ser una variante, pero C # actualmente no admite la especificación de variantes. Se espera que C # 4.0 sea compatible con las variantes, por lo que podría ser compatible en el futuro.

Para evitar esto ahora, puede utilizar el método de extensión LINQ Cast < object > () . Suponiendo que tiene un método llamado Foo que toma un IEnumerable < object > > . Puedes llamarlo así,

Foo(stringEnumerable.Cast<object>());

La forma más fácil de pasar IEnumerable < string > a la función que requiere IEnumerable < object > es a través de la función de conversión como esta:

public IEnumerable<object> convert<T>(IEnumerable<T> enumerable)
    {
    foreach (T o in enumerable)
        yield return o;
    }

Cuando salga C # 4, esto no será necesario, porque apoyará la covarianza y la contravarianza.

Deberías estar usando

IEnumerable<T> 

Si desea pasar diferentes tipos, puede consultar T para averiguar qué tipo es.

Una buena forma de pensar acerca de esto es preguntarse a sí mismo "¿Qué pasaría si pudiera hacer esto?". Tomemos el siguiente ejemplo:

IEnumerable<String> strings=...;
IEnumerable<Object> objects = strings; // assume this were legal

objects.Add(new Integer(5)); // what the...

Acabamos de agregar un número entero a una lista de cadenas. El compilador hace esto para preservar la seguridad del tipo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top