2009-09-25 14 views
65

Un compañero de trabajo me preguntó hoy cómo agregar un rango a una colección. Él tiene una clase que hereda de Collection<T>. Hay una propiedad de solo obtener de ese tipo que ya contiene algunos elementos. Él quiere agregar los artículos en otra colección a la colección de propiedades. ¿Cómo puede hacerlo en una forma amigable para C# 3? (Tenga en cuenta la restricción sobre la propiedad de solo obtener acceso, que impide soluciones como hacer un sindicato y reasignar).AddRange en una colección

Seguro, un foreach con la propiedad. Agregar funcionará Pero un AddRange de estilo List<T> sería mucho más elegante.

Es bastante fácil de escribir un método de extensión:

public static class CollectionHelpers 
{ 
    public static void AddRange<T>(this ICollection<T> destination, 
            IEnumerable<T> source) 
    { 
     foreach (T item in source) 
     { 
      destination.Add(item); 
     } 
    } 
} 

Pero tengo la sensación de que estoy reinventar la rueda. No encontré nada similar en System.Linq o morelinq.

¿Mal diseño? ¿Solo llamar Agregar? ¿Perdiendo lo obvio?

+3

Recuerde que la Q de LINQ es 'consulta' y se trata realmente de recuperación de datos, proyección, transformación, etc.La modificación de las colecciones existentes realmente no entra en el ámbito del propósito previsto de LINQ, razón por la cual LINQ no proporciona nada listo para usar para esto. Pero los métodos de extensión (y en particular su muestra) serían ideales para esto. – Levi

+0

Un problema, 'ICollection ' no parece tener un método 'Add'. http://msdn.microsoft.com/en-us/library/system.collections.icollection_methods(v=vs.100).aspx Sin embargo 'Collection ' tiene uno. –

+0

@TimGoodman: esa es la interfaz no genérica. Ver http://msdn.microsoft.com/en-us/library/92t2ye13.aspx – TrueWill

Respuesta

38

No, esto parece perfectamente razonable. Hay un método List<T>.AddRange() que básicamente hace esto, pero requiere que su colección sea concreta List<T>.

+0

Gracias; muy cierto, pero la mayoría de las propiedades públicas siguen las directrices de MS y no son listas. – TrueWill

+4

Sí, lo estaba dando más como razón de por qué no creo que haya un problema al hacer esto. Simplemente tenga en cuenta que será menos eficiente que la versión de la lista (ya que la lista puede preasignar) –

1

Las clases C5 Generic Collections Library son compatibles con el método AddRange. C5 tiene una interfaz mucho más robusta que realmente expone todas las características de sus implementaciones subyacentes y es compatible con la interfaz con las interfaces System.Collections.GenericICollection y IList, lo que significa que las colecciones C5 pueden sustituirse fácilmente como la implementación subyacente.

16

Recuerde que cada Add comprobará la capacidad de la colección y la cambiará de tamaño cada vez que sea necesario (más lento). Con AddRange, la colección se establecerá la capacidad y luego se agregarán los elementos (más rápido). Este método de extensión será extremadamente lento, pero funcionará.

+3

Para agregar a esto, también habrá una notificación de cambio de colección para cada adición, a diferencia de una notificación masiva con AddRange. –

0

Puede agregar su rango de IEnumerable a una lista y luego configurar ICollection = en la lista.

 IEnumerable<T> source; 

     List<item> list = new List<item>(); 
     list.AddRange(source); 

     ICollection<item> destination = list; 
+3

Si bien esto funciona de manera funcional, infringe las directrices de Microsoft para que las propiedades de recopilación sean de solo lectura (http://msdn.microsoft.com/en-us/library/ms182327.aspx) –

24

Pruebe a enviar a la lista en el método de extensión antes de ejecutar el ciclo. De esta forma, puede aprovechar el rendimiento de List.AddRange.

public static void AddRange<T>(this ICollection<T> destination, 
           IEnumerable<T> source) 
{ 
    List<T> list = destination as List<T>; 

    if (list != null) 
    { 
     list.AddRange(source); 
    } 
    else 
    { 
     foreach (T item in source) 
     { 
      destination.Add(item); 
     } 
    } 
} 
+0

Puede ser una pregunta un poco nona, pero ¿qué ocurre cuando la colección '' 'destination''' no se puede convertir en' '' List '' '? ¿'' 'List''' se convierte automáticamente en' '' null''' o se lanza una excepción? –

+1

El operador 'as' nunca lanzará. Si 'destination' no se puede convertir,' list' será nulo y se ejecutará el bloque 'else'. – rymdsmurf

+3

arrgggh! ¡Cambia la condición de las ramas, por el amor de todo lo que es santo! – nicodemus13

15

Desde .NET4.5 si quieres de una sola línea que can use System.Collections.Generic ForEach.

source.ForEach(o => destination.Add(o)); 

o incluso más corto que

source.ForEach(destination.Add); 

En cuanto al rendimiento es igual que para cada bucle (azúcar sintáctico).

también no lo hacen tratar asignándole como

var x = source.ForEach(destination.Add) 

causa ForEach es nula.

+5

Personalmente, estoy con Lippert en este: http://blogs.msdn.com/b/ericlippert/archive /2009/05/18/foreach-vs-foreach.aspx – TrueWill

+1

¿Debería ser source.ForEach (destination.Add)? – Frank

+0

@Frank Hola Frank, arreglado gracias por notar :) –

Cuestiones relacionadas