Что лучше при использовании IEnumerable с одним элементом: yield return или return []? - PullRequest
9 голосов
/ 22 ноября 2011

Это один из тех вопросов ", вы можете сделать это многими способами ". Рассмотрим следующий код:

protected virtual IEnumerable<ScriptReference> GetScriptReferences()
{
    ScriptReference referece = new ScriptReference();
    referece.Assembly = "FeyenoordEnabled";
    referece.Name = "FeyenoordEnabled.PassTextBox.js";

    return new ScriptReference[] { referece }; 
}

protected virtual IEnumerable<ScriptReference> GetScriptReferences()
{
    ScriptReference referece = new ScriptReference();
    referece.Assembly = "FeyenoordEnabled";
    referece.Name = "FeyenoordEnabled.PassTextBox.js";

    yield return referece;
}

Мне нужно только вернуть один товар. Первый фрагмент кода возвращает массив с одним элементом, а второй - с элементом. Что лучше и почему?

Ответы [ 3 ]

8 голосов
/ 22 ноября 2011

yield - довольно дорогое ключевое слово.Вы говорите компилятору сделать много.Если производительность не является проблемой, используйте более элегантный код.Но если производительность - проблема, придерживайтесь массива.

Я могу сказать из прошлого опыта, что избавление от этого типа yield принесло мне серьезный прирост производительности.Но, как всегда, профиль и найти реальные узкие места.

5 голосов
/ 22 ноября 2011

Профиль профиль профиль.Вот сравнение AB с использованием mono:

public static IEnumerable<int> UsingYield()
{
    yield return 42;
}
public static IEnumerable<int> ReturningArray()
{
    return new []{ 42 };
}

(скомпилировано с включенным -optimize+)

В версии yield создается класс, который реализует IEnumerable и весь shebang:

Примечание Я пропустил 163 строки кода CIL, реализующих тип блока анонимного типа «анонимный» Program/'<UsingYield>c__Iterator0'.Посмотрите все это здесь: https://gist.github.com/1384014

.method public static hidebysig 
       default class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> UsingYield ()  cil managed 
{
    .custom instance void class [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::'.ctor'() =  (01 00 00 00 ) // ....

    // Method begins at RVA 0x20f4
// Code size 16 (0x10)
.maxstack 3
.locals init (
    class Program/'<UsingYield>c__Iterator0'    V_0)
IL_0000:  newobj instance void class Program/'<UsingYield>c__Iterator0'::'.ctor'()
IL_0005:  stloc.0 
IL_0006:  ldloc.0 
IL_0007:  dup 
IL_0008:  ldc.i4.s 0xfffffffe
IL_000a:  stfld int32 Program/'<UsingYield>c__Iterator0'::$PC
IL_000f:  ret 
} // end of method Program::UsingYield

Версия массива кажется намного проще:

.method public static hidebysig 
       default class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> ReturningArray ()  cil managed 
{
    // Method begins at RVA 0x2110
// Code size 12 (0xc)
.maxstack 8
IL_0000:  ldc.i4.1 
IL_0001:  newarr [mscorlib]System.Int32
IL_0006:  dup 
IL_0007:  ldc.i4.0 
IL_0008:  ldc.i4.s 0x2a
IL_000a:  stelem.i4 
IL_000b:  ret 
} // end of method Program::ReturningArray

На фактической производительности во время выполнения, ПРОФИЛЬ ПРОФИЛЬ ПРОФИЛЬ!

4 голосов
/ 22 ноября 2011

Первый возвращается напрямую, когда вы вызываете его с созданным вами массивом.

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

Так что это действительно зависит от того, что вы хотите сделать, но просто помните, другое поведение.

...