remarks cref c#

c# - cref - No se puede convertir de List<DerivedClass> a List<BaseClass>



remarks c# (6)

Estoy tratando de pasar una lista de DerivedClass a una función que toma una lista de BaseClass , pero obtengo el error:

cannot convert from ''System.Collections.Generic.List<ConsoleApplication1.DerivedClass>'' to ''System.Collections.Generic.List<ConsoleApplication1.BaseClass>''

Ahora podría convertir mi List<DerivedClass> a List<BaseClass> , pero no me siento cómodo haciendo eso a menos que entiendo por qué el compilador no permite esto.

Las explicaciones que he encontrado simplemente han dicho que viola la seguridad de tipo de alguna manera, pero no lo estoy viendo. ¿Puede alguien ayudarme?

¿Cuál es el riesgo de que el compilador permita la conversión de List<DerivedClass> a List<BaseClass> ?

Aquí está mi SSCCE:

class Program { public static void Main() { BaseClass bc = new DerivedClass(); // works fine List<BaseClass> bcl = new List<DerivedClass>(); // this line has an error doSomething(new List<DerivedClass>()); // this line has an error } public void doSomething(List<BaseClass> bc) { // do something with bc } } class BaseClass { } class DerivedClass : BaseClass { }


Las explicaciones que he encontrado simplemente han dicho que viola la seguridad de tipo de alguna manera, pero no lo estoy viendo. ¿Cuál es el riesgo de que el compilador permita la conversión de List<DerivedClass> a List<BaseClass> ?

Esta pregunta se hace casi todos los días.

Una List<Mammal> no se puede convertir en una List<Animal> porque puedes poner una lagartija en una lista de animales . Una List<Mammal > no se puede convertir a una List<Giraffe> porque puede haber un tigre en la lista .

Por lo tanto, la List<T> tiene que ser invariable en T.

Sin embargo, List<Mammal> se puede convertir en IEnumerable<Animal> (a partir de C # 4.0) porque no hay ningún método en IEnumerable<Animal> que agregue un lagarto. IEnumerable<T> es covariante en T.


Cuando tiene una clase derivada de una clase base, los contenedores de esas clases no se derivan automáticamente. Entonces no puedes simplemente lanzar una List<Derived> a una List<Base> .

Utilice .Cast<T>() para crear una nueva lista donde cada objeto se devuelve a la clase base:

List<MyDerived> list1 = new List<MyDerived>(); List<MyBase> list2 = list1.Cast<MyBase>().ToList();

Tenga en cuenta que esta es una lista nueva, no una versión de lanzamiento de su lista original, por lo que las operaciones en esta nueva lista no se reflejarán en la lista original. Las operaciones en los objetos contenidos reflejarán, sin embargo.


El comportamiento que está describiendo se llama covarianza : si A es B , entonces la List<A> es List<B> .

Sin embargo, para tipos mutables como List<T> , eso es fundamentalmente inseguro.

Si esto hubiera sido posible, el método podría agregar un new OtherDerivedClass() a una lista que solo puede contener DerivedClass .

La covarianza es segura en tipos inmutables, aunque .Net solo la admite en interfaces y delegados.
Si cambia el parámetro List<T> a IEnumerable<T> , eso funcionará


Es porque List<T> está in-variant , no co-variant IEnumerable<T> , por lo que debe cambiar a IEnumerable<T> que admite la IEnumerable<T> , debería funcionar:

IEnumerable<BaseClass> bcl = new List<DerivedClass>(); public void doSomething(IEnumerable<BaseClass> bc) { // do something with bc }

Información sobre la covariante en genéricos


Si pudieras escribir

List<BaseClass> bcl = new List<DerivedClass>();

puedes llamar

var instance = new AnotherClassInheritingFromBaseClass(); bc1.Add(instance);

Agregar una instancia que no es un DerivedClass a la lista.


Una solución que utilicé fue crear una clase de extensión:

public static class myExtensionClass { public void doSomething<T>(List<BaseClass> bc) where T: BaseClass { // do something with bc } }

Utiliza genérico, pero cuando lo llamas no tienes que especificar la clase ya que ya ''le dijiste'' al compilador que el tipo es el mismo de la clase extendida.

Lo llamarías de esta manera:

List<DerivedClass> lst = new List<DerivedClass>(); lst.doSomething();