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 entonces...
List<string> sl = new List<string>();
List<object> ol;
ol = (List<object>)sl;
resultados en No se puede convertir el tipo System.Collections.Generic.List<string>
aSystem.Collections.Generic.List<object>
Por supuesto, puede hacerlo sacando todo de la lista de cadenas y volviéndolo a colocar uno a la vez, pero es una solución bastante complicada.
c#
.net
generics
covariance
type-safety
Matt Sheppard
fuente
fuente
Respuestas:
Piénselo de esta manera, si tuviera que hacer tal conversión y luego agregar un objeto de tipo Foo a la lista, la lista de cadenas ya no es consistente. Si tuviera que iterar la primera referencia, obtendría una excepción de conversión de clase porque una vez que golpeó la instancia de Foo, ¡Foo no podría convertirse en cadena!
Como nota al margen, creo que sería más significativo 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 # en un tiempo, así que no sé si eso es legal, pero ese tipo de transmisión es realmente (potencialmente) útil. En este caso, se pasa 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 un elenco de este tipo es legal en C #?
fuente
ol
tuviera algo que no fuera una cadena, supongo que esperaría que el elenco fallara en tiempo de ejecución. Pero donde realmente te meterías en problemas es si el elenco tuvo éxito y luego se agregó algool
que no es una cadena. Debido a que hacesl
referencia al mismo objeto, ahora tuList<string>
contendría una no cadena. ElAdd
es el problema, que supongo que justifica por qué este código no se compilará, pero se compilará si cambiaList<object> ol
aIEnumerable<object> ol
, que no tiene una extensiónAdd
. (Verifiqué esto en C # 4.)InvalidCastException
porque el tipo de tiempo de ejecución deol
sigue siendoList<object>
.Si está utilizando .NET 3.5, eche un vistazo al método Enumerable.Cast. Es un método de extensión, por lo que puede 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 señaló Zooba, puede llamar a ol.ToList () para obtener una lista
fuente
No puede lanzar entre tipos genéricos con diferentes parámetros de tipo. Los tipos genéricos especializados no forman parte del mismo árbol de herencia y también lo son los 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();
fuente
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 queList<string>()
el compilador diceListString()
(que contiene cadenas). [Gente técnica: esta es una versión en inglés extremadamente simple de lo que está sucediendo]En consecuencia, obviamente, el compilador no puede ser lo suficientemente inteligente como para convertir un ListString en un ListObject lanzando 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 pasar de uno a otro.
fuente
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 lanzar a un tipo más general como un objeto. Y como lo indicó Rex, el objeto List no convertirá cada objeto por usted.
Es posible que desee 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.
fuente
Sí, puede, desde .NET 3.5:
List<string> sl = new List<string>(); List<object> ol = sl.Cast<object>().ToList();
fuente
Mike: creo que la contravarianza tampoco está permitida en C #
Consulte Variación de parámetro de tipo genérico en CLR para obtener más información.
fuente
Creo que esta (contravarianza) será compatible con C # 4.0. http://blogs.msdn.com/charlie/archive/2008/10/27/linq-farm-covariance-and-contravariance-in-visual-studio-2010.aspx
fuente
Eso es en realidad para que no intente poner ningún "objeto" extraño en su variante de lista "ol" (como
List<object>
parece permitir), porque su código fallaría entonces (porque la lista realmente lo esList<string>
y solo aceptará objetos de tipo String ). Es por eso que no puede convertir su variable a una especificación más general.En Java es al revés, no tienes genéricos, y en su lugar todo es Lista de objetos en tiempo de ejecución, y realmente puedes incluir cualquier objeto extraño en tu Lista supuestamente estrictamente escrita. Busque "Genéricos reificados" para ver una discusión más amplia sobre el problema de Java ...
fuente
Tal covarianza en genéricos no es compatible, pero en realidad puede hacer esto con matrices:
object[] a = new string[] {"spam", "eggs"};
C # realiza comprobaciones en tiempo de ejecución para evitar que coloque, digamos, un
int
archivo ena
.fuente
Aquí hay otra solución anterior a.NET 3.5 para cualquier IList cuyo contenido se pueda emitir implícitamente.
public IList<B> ConvertIList<D, B>(IList<D> list) where D : B { List<B> newList = new List<B>(); foreach (D item in list) { newList.Add(item); } return newList; }
(Basado en el ejemplo de Zooba)
fuente
Tengo un:
private List<Leerling> Leerlingen = new List<Leerling>();
Y lo iba a llenar con datos recopilados en un
List<object>
Lo que finalmente funcionó para mí fue este:.Cast
al tipo que desea obtenerIEnumerable
de ese tipo, luego encasillaIEnemuerable
alList<>
que desea.fuente
Mm, gracias a los comentarios anteriores encontré dos formas de averiguarlo. El primero es obtener la lista de cadenas de elementos y luego convertirla en la lista de objetos IEnumerable:
IEnumerable<object> ob; List<string> st = new List<string>(); ob = st.Cast<object>();
Y el segundo es evitar el tipo de objeto IEnumerable, simplemente lanzando la cadena al tipo de objeto y luego usando la función "toList ()" en la misma oración:
List<string> st = new List<string>(); List<object> ob = st.Cast<object>().ToList();
Me gusta más la segunda forma. Espero que esto ayude.
fuente
List<string> sl = new List<string>(); List<object> ol; ol = new List<object>(sl);
fuente