2012-03-30 12 views
25

Solo estoy pensando en el diseño y el rendimiento. Anteriormente solía escribir algo como,nuevo [] o nuevo Lista <T>?

var strings = new List<string> { "a", "b", "c" }; 
var ints = new List<int> { 1, 2, 3}; 

Pero ahora me suelen gustar este estilo más,

var strings = new [] { "a", "b", "c" }.ToList(); 
var ints = new [] { 1, 2, 3}.ToList(); 

Yo prefiero el segundo estilo, pero ahora teniendo en cuenta - es lo que realmente vale la pena escribirlo como ¿o tal vez no es tan eficiente y requiere más operaciones?

+0

también prefiero el segundo estilo – Magrangs

+8

¿por qué prefiere la segunda ¿estilo? Creo que es menos legible, especialmente cuando se usa 'var'. –

+0

¿Has probado algunas pruebas de rendimiento? simplemente cronometralo y hazlo 100 veces para ver los valores de avarage de esa prueba. – Frederiek

Respuesta

42

No estoy de acuerdo con Darin: son no equivalente en términos de rendimiento. La última versión tiene que crear una nueva matriz, y ToList la copiará en la nueva lista. La versión recogida inicializador es equivalente a:

var tmp = new List<int>(); 
tmp.Add(1); 
tmp.Add(2); 
tmp.Add(3); 
var ints = tmp; 

Asumiendo la lista comienza con un buffer lo suficientemente grande, que no requerirá ninguna nueva asignación - a pesar de que se implican un par de llamadas a métodos. Si hace esto para un número muy grande , entonces requerirá más asignaciones que la versión ToList, porque copiará los elementos como va.

La diferencia de rendimiento es probable que sea insignificante, pero es distinto de cero (y no claramente mejor en cualquier dirección - hay un menor número de llamadas en la versión de serie, pero más de la asignación).

Me concentraría más en el estilo que en el rendimiento a menos que tenga una razón para sospechar que la diferencia es significativa, en cuyo caso debe medir en lugar de adivinar.

Personalmente prefiero el primer formulario, creo que aclara que está utilizando una lista desde el principio. Otra alternativa sería la de escribir su propia clase estática:

public static class Lists 
{ 
    public static List<T> Of<T>(T item0) 
    { 
     return new List<T> { item0 }; 
    } 

    public static List<T> Of<T>(T item0, T item1) 
    { 
     return new List<T> { item0, item1 }; 
    } 

    public static List<T> Of<T>(T item0, T item1, T item2) 
    { 
     return new List<T> { item0, item1, item2 }; 
    } 

    ... as many times as you really care about, then ... 

    public static List<T> Of<T>(params T[] items) 
    { 
     return items.ToList(); 
    } 
} 

entonces usted puede escribir:

var ints = Lists.Of(1); 
var ints = Lists.Of(1, 2, 3); 
var ints = Lists.Of(1, 2, 3, 5, 6, 7, 8); // Use the params version 

Esto todavía deja claro que está utilizando las listas, pero se aprovecha de la inferencia de tipos.

Usted también puede considerar que una exageración aunque :)

+0

1+. ¡Eso es lo que quería decir! Se está produciendo una gran cantidad de copia de matriz y ** no es gratuita **. – Aliostad

+0

entonces, ¿quieres decir que la primera versión debería funcionar mejor? –

+0

Absolutamente, no creará el doble de los elementos requeridos ni realizará operaciones adicionales. El hecho es que esta diferencia es tan leve que no te preocupes al inicializar las listas solo una vez, pero pueden diferir con el tiempo si las creas muchas veces. Pero estamos hablando de pequeñas diferencias en cualquier caso. – Jack

6

Supongo que en el primer caso los elementos se agregan automáticamente a la lista, mientras que en el segundo primero se crea una matriz, luego se itera y cada elemento se agrega a la lista.

Aunque probablemente segundo será optimizado para evitar una verdadera creación de la matriz vale la pena notar que si esta operación se lleva a cabo dentro de un bucle probablemente el doble de la cantidad de asignación de objetos (a menos que sea recto optimizado), sin necesidad real de Haz eso.

Debe comprobar bytecode para estar seguro de ello.

No me gustan los internos de C#, así que tómenlo con cuidado.

+0

+1 para 'dos veces la asignación de objetos' –

13

Dejando a un lado la diferencia entre las dos desde una perspectiva de rendimiento, la primera expresa lo que estás tratando de lograr de una mejor manera.

Considere expresar el código en Inglés:

declarar una lista de cadenas con estos contenidos

Y

declarar una matriz de cadenas con estos contenidos y luego convertirlo en una lista de cadenas

Para mí, el primero parece más natural. Aunque reconozco que el segundo puede ser mejor para ti.

+0

Eso es exactamente lo que pensé un segundo antes de leer su respuesta :) –

7

Ejemplo 1 (enteros var = nueva lista {1, 2, 3};): Proporciona un 31,5% en cabeza (Eumerable.ToList) y List.Add () causa un 8.7% de sobrecarga.

Donde como ejemplo 2: Causa un 11.8% de sobrecarga en List.ctor y un 5% para Ensure Capacity.

(Resultados de la puerta de hormigas rojas de rendimiento Profiler)

Se puede ver que enteros var = nueva lista {1, 2, 3}; tiene más operaciones para realizar a través del desmontaje

var intsx = new[] {1, 2, 3}.ToList(); 
0000003f mov   edx,3 
00000044 mov   ecx,60854186h 
00000049 call  FFF5FD70 
0000004e mov   dword ptr [ebp-4Ch],eax 
00000051 lea   ecx,[ebp-50h] 
00000054 mov   edx,872618h 
00000059 call  61490806 
0000005e lea   eax,[ebp-50h] 
00000061 push  dword ptr [eax] 
00000063 mov   ecx,dword ptr [ebp-4Ch] 
00000066 call  614908E3 
0000006b mov   ecx,dword ptr [ebp-4Ch] 
0000006e call  dword ptr ds:[008726D8h] 
00000074 mov   dword ptr [ebp-54h],eax 
00000077 mov   eax,dword ptr [ebp-54h] 
0000007a mov   dword ptr [ebp-40h],eax 

var ints = new List<int> { 1, 2, 3 }; 
0000007d mov   ecx,60B59894h 
00000082 call  FFF5FBE0 
00000087 mov   dword ptr [ebp-58h],eax 
0000008a mov   ecx,dword ptr [ebp-58h] 
0000008d call  60805DB0 
00000092 mov   eax,dword ptr [ebp-58h] 
00000095 mov   dword ptr [ebp-48h],eax 
00000098 mov   ecx,dword ptr [ebp-48h] 
0000009b mov   edx,1 
000000a0 cmp   dword ptr [ecx],ecx 
000000a2 call  608070C0 
000000a7 nop 
000000a8 mov   ecx,dword ptr [ebp-48h] 
000000ab mov   edx,2 
000000b0 cmp   dword ptr [ecx],ecx 
000000b2 call  608070C0 
000000b7 nop 
000000b8 mov   ecx,dword ptr [ebp-48h] 
000000bb mov   edx,3 
000000c0 cmp   dword ptr [ecx],ecx 
000000c2 call  608070C0 
000000c7 nop 
000000c8 mov   eax,dword ptr [ebp-48h] 
000000cb mov   dword ptr [ebp-44h],eax 
     } 
+3

¿Qué hacen estas 'llamadas'? ¿Cuáles son sus costos? –

+0

No tengo ninguna herramienta de creación de perfiles instalada para proporcionar los costos precisos, esta es la salida bruta del desmontaje que le muestra cuántas operaciones se realizan para ambos ejemplos en la pregunta. Se instalará y editará. –

+0

@ L.B actualizó mi respuesta para proporcionar los costos que estaba buscando. –

1

Me gustan las primeras versiones. Pero respecto del perfomance creo que lo mejor es utilizar la matriz y definir específicamente el número de elementos va a ser si, por supuesto, esto puede ser posible:

var x = new int[3] { 1, 3, 3 }.ToList(); 
+0

No creo que especifique explícitamente que el tamaño de la matriz agregue algo. El compilador puede determinarlo a través del análisis estático; y la 'Lista ' se creará en cualquier tamaño que el constructor prefiera en cualquier caso. –

Cuestiones relacionadas