En C#, ¿por qué no se puede almacenar un objeto List<string> en una variable List<object>?

Resuelto Matt Sheppard asked hace 16 años • 14 respuestas

Parece que un objeto List no se puede almacenar en una variable List en C#, y ni siquiera se puede convertir explícitamente de esa manera.

List<string> sl = new List<string>();
List<object> ol;
ol = sl;

da como resultado No se puede convertir implícitamente el tipo System.Collections.Generic.List<string>aSystem.Collections.Generic.List<object>

Y luego...

List<string> sl = new List<string>();
List<object> ol;
ol = (List<object>)sl;

da como resultado No se puede convertir el tipo System.Collections.Generic.List<string>aSystem.Collections.Generic.List<object>

Por supuesto, puedes hacerlo sacando todo de la lista de cadenas y volviéndolo a colocar uno a la vez, pero es una solución bastante complicada.

Matt Sheppard avatar Aug 09 '08 09:08 Matt Sheppard
Aceptado

Piénselo de esta manera, si hiciera dicha conversión y luego agregara un objeto de tipo Foo a la lista, la lista de cadenas ya no sería consistente. Si iteraras la primera referencia, obtendrías una excepción de conversión de clase porque una vez que llegas a la instancia de Foo, ¡Foo no se puede convertir en una cadena!

Como nota al margen, creo que sería más importante si puedes o no hacer el reparto inverso:

List<object> ol = new List<object>();
List<string> sl;
sl = (List<string>)ol;

No he usado C# desde hace tiempo, así que no sé si eso es legal, pero ese tipo de conversión es realmente (potencialmente) útil. En este caso, estás pasando de una clase (objeto) más general a una clase (cadena) más específica que se extiende desde la general. De esta manera, si agrega a la lista de cadenas, no está violando la lista de objetos.

¿Alguien sabe o puede probar si dicha conversión es legal en C#?

Mike Stone avatar Aug 09 '2008 02:08 Mike Stone

Si está utilizando .NET 3.5, eche un vistazo al método Enumerable.Cast. Es un método de extensión por lo que puedes llamarlo directamente en la Lista.

List<string> sl = new List<string>();
IEnumerable<object> ol;
ol = sl.Cast<object>();

No es exactamente lo que pediste, pero debería funcionar.

Editar: como lo señaló Zooba, luego puede llamar a ol.ToList() para obtener una lista

Ray avatar Aug 09 '2008 03:08 Ray

No se puede convertir entre tipos genéricos con diferentes parámetros de tipo. Los tipos genéricos especializados no forman parte del mismo árbol de herencia y, por lo tanto, son tipos no relacionados.

Para hacer esto antes de NET 3.5:

List<string> sl = new List<string>();
// Add strings to sl

List<object> ol = new List<object>();

foreach(string s in sl)
{
    ol.Add((object)s);  // The cast is performed implicitly even if omitted
}

Usando Linq:

var sl = new List<string>();
// Add strings to sl

var ol = new List<object>(sl.Cast<object>());

// OR
var ol = sl.Cast<object>().ToList();

// OR (note that the cast to object here is required)
var ol = sl.Select(s => (object)s).ToList();
Zooba avatar Aug 10 '2008 11:08 Zooba

La razón es que una clase genérica como List<>, para la mayoría de los propósitos, se trata externamente como una clase normal. por ejemplo, cuando dice List<string>()que el compilador dice ListString()(que contiene cadenas). [Gente técnica: esta es una versión en inglés extremadamente sencilla de lo que está pasando]

En consecuencia, obviamente el compilador no puede ser lo suficientemente inteligente como para convertir un ListString en un ListObject emitiendo los elementos de su colección interna.

Es por eso que existen métodos de extensión para IEnumerable como Convert() que le permiten proporcionar fácilmente la conversión de los elementos almacenados dentro de una colección, lo que podría ser tan simple como convertir de uno a otro.

Rex M avatar Aug 09 '2008 02:08 Rex M

Esto tiene mucho que ver con la covarianza; por ejemplo, los tipos genéricos se consideran parámetros y si los parámetros no se resuelven correctamente en un tipo más específico, la operación falla. La implicación de esto es que realmente no se puede convertir a un tipo de objeto más general. Y como lo indicó Rex, el objeto Lista no convertirá cada objeto por usted.

Quizás quieras probar el código ff en su lugar:

List<string> sl = new List<string>();
//populate sl
List<object> ol = new List<object>(sl);

o:

List<object> ol = new List<object>();
ol.AddRange(sl);

ol copiará (teóricamente) todo el contenido de sl sin problemas.

Jon Limjap avatar Aug 09 '2008 02:08 Jon Limjap