2012-03-30 2 views
25

Я просто думаю о стилизации и исполнении. Раньше я использовал, чтобы написать что-то вроде,новый [] или новый список <T>?

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

Но теперь я, как правило, нравится этот стиль более,

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

Я предпочитаю второй стиль, но сейчас рассматривает - это действительно стоит писать его как что, может быть, это не так эффективно и требует больше операций?

+0

Я также предпочитаю 2-й стиль – Magrangs

+8

почему вы предпочитаете второй стиль? Я думаю, что это менее читаемо, особенно при использовании 'var'. –

+0

Вы пробовали какие-то тесты производительности на нем? просто время и сделайте это в 100 раз, чтобы увидеть значения avarage из этого теста. – Frederiek

ответ

42

Я не согласен с Дарином: они не эквивалент с точки зрения производительности. Последняя версия должна создать новый массив, и ToList затем скопирует его в новый список. Версия коллекции инициализатора эквивалентна:

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

Предполагая, что список начинается с достаточно большим буфером, который не будет требовать дальнейшего распределения - хотя будет включать несколько вызовов методов. Если вы сделаете это за очень большое количество , то потребуется больше ассигнований, чем ToList, так как оно скопирует элементы по мере их поступления.

Разница в производительности является, вероятно, будет незначительным, но это не ноль (а не четко лучше в любом направлении - есть меньше вызовов в версии массива, но больше распределения).

Я бы больше сосредоточился на стиле, чем на рабочем месте, если у вас нет причин подозревать, что разница значительна, и в этом случае вы должны измерить, а не просто гадать.

Лично я предпочитаю первую форму - я думаю, что она дает понять, что вы используете список с самого начала. Другой альтернативой было бы написать свой собственный статический класс:

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(); 
    } 
} 

Тогда вы можете написать:

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 

Это все еще дает понять, что вы используете списки, но имеет преимущество логического вывода типа.

Вы можете хорошо рассмотреть его излишеством, хотя :)

+0

1+. Вот что я хотел сказать! Многое копирование массивов происходит, и это не бесплатно **. – Aliostad

+0

Итак, вы имеете в виду, что первая версия должна «работать» лучше, не так ли? –

+0

Абсолютно, он не будет создавать дважды предметы, которые не требуются, и выполнять дополнительные операции. Дело в том, что эта разница настолько мала, что вы не будете беспокоиться об этом при инициализации списков только один раз, но они могут отличаться со временем, если вы создаете их много раз. Но мы говорим о небольших различиях в любом случае. – Jack

6

Я предполагаю, что в первом случае элементы автоматически добавляются в список, а во втором сначала создается массив, после чего он повторяется и каждый элемент добавляется в список.

Хотя, вероятно, второй один будет оптимизирован, чтобы избежать реального создания массива он стоит заметить, что, если эта операция выполняется в цикле вы, вероятно, в два раза больше выделения объекта (если это не прямолинейно оптимизировано) без реальной необходимости сделай это.

Вы должны проверить байт-код, чтобы быть в этом уверен.

Я не люблю внутренние элементы C#, хотя и берем это с осторожностью!

+0

+1 для 'дважды выделение объекта –

13

Отметив разницу между двумя с точки зрения эффективности, первая выражает то, чего вы пытаетесь достичь в лучшем виде.

Рассмотрим выражающий код на английском языке:

объявить список строк с этим содержимым

И

объявить массив строк с этим содержимым, а затем преобразовать его в список строк

Мне кажется, что первое кажется более естественным. Хотя я признаю, что второй может быть лучше для вас.

+0

Это именно то, что я думал за секунду до того, как я прочитал ваш ответ :) –

7

Пример 1 (вар Интс = новый список {1, 2, 3};): Обеспечивает 31,5% накладных расходов (Eumerable.ToList) и List.Add () вызывает 8,7% накладных расходов.

Где в качестве примера 2: Накладные расходы на List.ctor составляют 11,8% и 5% для обеспечения емкости.

(Результаты Red Gate ANTS Performance Profiler)

Вы можете увидеть, что вар INTS = новый список {1, 2, 3}; имеет больше операций для выполнения при разборке

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

Что они делают? каковы их издержки? –

+0

У меня нет каких-либо инструментов для профилирования, чтобы обеспечить точные затраты, это необработанный результат разборки, показывающий, сколько операций выполняется для обоих примеров в вопросе. Будет устанавливать и редактировать. –

+0

@ L.B обновил мой ответ, чтобы обеспечить расходы, которые вы искали. –

1

Мне нравятся первые версии. Но о Perfomance я думаю, что лучше всего использовать массив и определить конкретно количество элементов будет, если, конечно, это может быть возможным:

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

Я не думаю, что явное указание размера массива добавляет что-либо. Компилятор может определить его посредством статического анализа; и «Список » будет создан в любом размере, который предпочитает конструктор в любом случае. –

Смежные вопросы