cómo agregar un rango de elementos a la variable IList

81

no hay AddRange()método para IList<T>.

¿Cómo puedo agregar una lista de elementos a IList<T>sin iterar a través de elementos y usar el Add()método?

mohsen dorparasti
fuente

Respuestas:

65

AddRangese define en List<T>, no la interfaz.

Puede declarar la variable como en List<T>lugar de IList<T>o convertirla List<T>en para obtener acceso a AddRange.

((List<myType>)myIList).AddRange(anotherList);

Esta no es una buena práctica (vea los comentarios a continuación), ya que IList<T>puede que no sea ​​un List<T>, pero algún otro tipo que implementó la interfaz y puede que no tenga un AddRangemétodo; en tal caso, solo lo descubrirá cuando su código arroje un excepción en tiempo de ejecución.

Por lo tanto, a menos que sepa con certeza que el tipo es de hecho List<T>, no debería intentar usarlo AddRange.

Una forma de hacerlo es probando el tipo con los operadores is o como (desde C # 7).

if(myIList is List<T>)
{
   // can cast and AddRange
}
else
{
   // iterate with Add
}
Oded
fuente
2
@ mohsen.d: si se genera el tipo, no desea cambiar el código generado (ya que es probable que se sobrescriba). ConcatEmite o usa LINQ , como respondió @Self_Taught_Programmer .
Oded
2
@ mohsen.d - Si es su código, también podría declarar el tipo como List<T>(o, si esta no es una buena opción para usted, hacer el reparto donde necesita AddRangepara mantenerlo localizado - es una operación de muy bajo costo ).
Oded
53
No no no. La razón por la que, para empezar, es una IList <T> es porque puede ser algo más que una implementación de List <T>. Escriba un método de extensión como lo muestra BlackjacketMack si realmente necesita un método AddRange.
Derek Greer
6
No tengo idea de por qué esto recibió tantos votos positivos, ya que claramente arrojará algo similar InvalidCastExceptionsi se usa en cualquier otra cosa que no sea List<T>(matriz, por ejemplo).
bashis
3
Pero el casting no es una buena idea. Puede generar gastos generales de rendimiento.
Gul Md Ershad
77

Si observa el código fuente de C # para List , creo que List.AddRange () tiene optimizaciones que un simple bucle no aborda. Por lo tanto, un método de extensión simplemente debe verificar si IList es una Lista, y si es así, use su AddRange () nativo.

Al hurgar en el código fuente, ve que la gente de .NET hace cosas similares en sus propias extensiones Linq para cosas como .ToList () (si es una lista, envíelo ... de lo contrario, créelo).

public static class IListExtension
{
    public static void AddRange<T>(this IList<T> list, IEnumerable<T> items)
    {
        if (list == null) throw new ArgumentNullException(nameof(list));
        if (items == null) throw new ArgumentNullException(nameof(items));

        if (list is List<T> asList)
        {
            asList.AddRange(items);
        }
        else
        {
            foreach (var item in items)
            {
                list.Add(item);
            }
        }
    }
}
BlackjacketMack
fuente
5
Desde el punto de vista de la optimización, en realidad está enviando lista List<T>dos veces aquí. Uno de los cuales podría optimizarse con la aspalabra clave.
bashis
Buena llamada @bashis. Siempre voy y vengo sobre el costo de una limpieza de doble yeso vs. GC de nuestra nueva var. Pero de hecho podríamos hacer var listCasted = list como List <T>; if (listCasted! = null) ... Quizás las expresiones de declaración de c # 6 cambien este patrón: if (myVar.As (out myVarCasted)) myVarCasted ...)
BlackjacketMack
¿Puede actualizar el código con la optimización mencionada? Todavía no soy tan fluido en las funciones más nuevas. @BlackjacketMack
2
@zuckerthoben: acabo de ejecutar una prueba con un millón de iteraciones utilizando ambos enfoques y no hubo diferencia en el rendimiento. Entonces, no lo llamaría una optimización ... además, agrega una línea de código (pero reduce la conversión de un parens). De todos modos, probablemente usaría 'as' en estos días: var listCasted = list as List <T>; if (listCasted! = null) {listCasted.AddRange (elementos);}. No vale la pena actualizar la respuesta en mi humilde opinión, pero es bueno presentarla como una alternativa sintáctica.
BlackjacketMack
2
A partir de ahora puedes hacerloif (list is List<T> castedList) { castedList.AddRange(items); }
André Mantas
23

Podrías hacer algo como esto:

IList<string> oIList1 = new List<string>{"1","2","3"};
IList<string> oIList2 = new List<string>{"4","5","6"};
IList<string> oIList3 = oIList1.Concat(oIList2).ToList();

Entonces, básicamente, usaría la Concat()extensión y ToList()obtendría una funcionalidad similar a AddRange().

Fuente

Rayshawn
fuente
1
El problema con su enfoque es que Enumerable.Concatestá implementado por System.Linq.Enumerabley el valor de retorno de ese método es IEnumerable<TSource>, por lo que creo que no debería devolverse IList<TSource>, podría devolver algo más debido a los detalles de implementación que no conocemos sin verificar el código fuente. aunque no hay garantía de que no cambie, por lo que se debe prestar especial atención al admitir varias versiones.
jweyrich
8

También puede escribir un método de extensión como este:

internal static class EnumerableHelpers
{
    public static void AddRange<T>(this IList<T> collection, IEnumerable<T> items)
    {
        foreach (var item in items)
        {
            collection.Add(item);
        }
    }
}

Uso:

        IList<int> collection = new int[10]; //Or any other IList
        var items = new[] {1, 4, 5, 6, 7};
        collection.AddRange(items);

Que todavía está iterando sobre elementos, pero no tiene que escribir la iteración o lanzar cada vez que lo llama.

bashis
fuente
1

Otra respuesta usando LINQ, siempre que lo que está agregando sea un List<T>o pueda llamarlo ToList():

IEnumerable<string> toAdd = new string[] {"a", "b", "c"};
IList<string> target = new List<string>();

toAdd.ToList().ForEach(target.Add);
Lluvia roja
fuente
-2
var var1 = output.listDepartment1
var var2 = output.listDepartment2  
var1.AddRange(var2);
var list = var1;
Siva Ragu
fuente