Pregunta

Si tengo estas dos clases:

class A {}
class B : A {}

y hago una Lista < A > pero quiero agregar una Lista < B > para ello llamando a List < A > .AddRange (List < B >) pero el compilador se niega:

Argument '1': cannot convert from 'System.Collections.Generic.List<A>'
to 'System.Collections.Generic.IEnumerable<B>

que entiendo completamente porque IEnumerable < B > no hereda de IEnumerable < A > ;, su tipo genérico tiene la herencia.

Mi solución es enumerar a través de List < B > y agrega elementos individualmente porque List < A > .Add (A item) funcionará con los elementos B:

foreach(B item in listOfBItems)
{
    listOfAItems.Add(item);
}

Sin embargo, eso no es expresivo porque lo que quiero es solo AddRange.

Podría usar

List<B>.ConvertAll<A>(delegate(B item) {return (A)item;});

pero eso es innecesariamente complicado y un nombre inapropiado porque no estoy convirtiendo, estoy lanzando.

Pregunta : Si escribiera mi propia colección tipo Lista, ¿qué método agregaría para permitirme copiar una colección de B en una colección de A como una sola línea? similar a List < A > .AddRange (List < B >) y retienen la máxima seguridad de escritura. (Y, como máximo, quiero decir que el argumento es tanto una recopilación como una comprobación de herencia de tipo).

¿Fue útil?

Solución

De hecho, los tipos genéricos no son variantes en este momento. En C # 4.0, IEnumerable <B & Gt; será convertible a IEnumerable <A > si B es convertible a A mediante una conversión de referencia . Para obtener algunos detalles sobre el diseño de esta función, consulte:

http://blogs.msdn.com/ ericlippert / archive / tags / Covarianza + y + Contravarianza / default.aspx

Otros consejos

Esto desafortunadamente no funciona porque los genéricos en .net (todavía) no admiten covarianza.

Sin embargo, puede crear un pequeño método o clase auxiliar para superar este problema.

Si implementa su propia clase de lista, puede agregar covarianza utilizando un parámetro genérico adicional:

class MyList<T> {
    void AddRange<U>(IEnumerable<U> items) where U: T {
        foreach (U item in items) {
            Add(item);
        }
    }
}

¿No puedes simplemente hacer:

listOfAItems.AddRange(listOfBItems.Cast<A>());

Pude lograr esto usando LINQ ...

listOfAItems.AddRange(listOfBItems.Cast<A>());

En caso de que se encuentre en una situación en la que los tipos genéricos no son variantes, el siguiente método de extensión puede facilitarle la vida:

public static void AddRange<TList,TOther>(this List<TList> list, IEnumerable<TOther> collection) where TOther: TList {
    foreach(TOther e in collection) {
        list.Add(e);
    }
}

En lugar de tener que derivar de List<T> o tener este método en alguna clase de utilidad, usarlo como un método de extensión simplifica el uso. También puede beneficiarse de la inferencia, por lo que esta llamada anteriormente inválida será válida sin ninguna modificación:

List<Animal> animals;
List<Dog> dogs;
animals.AddRange(dogs);

Lo único que se me ocurre es esto

public class MyList<T> : List<T>
{
    public void AddRange<Tother>(IEnumerable<Tother> col)
        where Tother: T
    {    
        foreach (Tother item in col)
        {
            this.Add(item);
        }
    }
}

Llamarlo significa hacer MyList < A > .AddRange < B > (MyList < B >). Esto falla si el argumento no es enumerable o si la herencia de tipo no funciona, por lo que satisface el requisito de seguridad de tipo máximo de mi pregunta.

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