Действие для делегирования: новое действие или действие?
Я нашел два разных способа инициализации делегата с действием:
Создайте новое действие или кастинг для Action.
Delegate foo = new Action(() => DoNothing(param));
Delegate bar = (Action)(() => DoNothing(param));
Есть ли разница между этими двумя синтаксисами?
Какой из них лучше и почему?
Делегат используется в этом примере, потому что синтаксис полезен для вызова таких методов, как BeginInvoke или Invoke, с помощью выражения лямбда, и важно, чтобы выражение лямбда превратилось в действие
static main
{
Invoke((Action)(() => DoNothing())); // OK
Invoke(new Action(() => DoNothing())); // OK
Invoke(() => DoNothing()); // Doesn't compil
}
private static void Invoke(Delegate del) { }
Но интересно, что компилятор разрешил это:
Action action = () => DoNothing();
Invoke(action);
Ответы
Ответ 1
Между этими двумя инструкциями нет разницы. В обеих инструкциях создается новый экземпляр Action.
Ниже приведен код IL, подтверждающий это.
Консольная программа:
class Program
{
static void Main(string[] args)
{
Delegate barInit = (Action)(() => DoNothing());
Delegate fooInit = new Action(() => DoNothing());
}
private static void DoNothing() { }
}
IL Код:
// First instruction
IL_0000: ldsfld class [mscorlib]System.Action CodeMachineTest.Program::'CS$<>9__CachedAnonymousMethodDelegate2'
IL_0005: brtrue.s IL_0018
IL_0007: ldnull
IL_0008: ldftn void CodeMachineTest.Program::'<Main>b__0'()
// Create a new Action instance for the instruction (Action)(() => DoNothing())
IL_000e: newobj instance void [mscorlib]System.Action::.ctor(object, native int)
IL_0013: stsfld class [mscorlib]System.Action CodeMachineTest.Program::'CS$<>9__CachedAnonymousMethodDelegate2'
IL_0018: ldsfld class [mscorlib]System.Action CodeMachineTest.Program::'CS$<>9__CachedAnonymousMethodDelegate2'
IL_001d: pop
// Second instruction
IL_001e: ldsfld class [mscorlib]System.Action CodeMachineTest.Program::'CS$<>9__CachedAnonymousMethodDelegate3'
IL_0023: brtrue.s IL_0036
IL_0025: ldnull
IL_0026: ldftn void CodeMachineTest.Program::'<Main>b__1'()
IL_002c: newobj instance void [mscorlib]System.Action::.ctor(object, native int)
IL_0031: stsfld class [mscorlib]System.Action CodeMachineTest.Program::'CS$<>9__CachedAnonymousMethodDelegate3'
IL_0036: ldsfld class [mscorlib]System.Action CodeMachineTest.Program::'CS$<>9__CachedAnonymousMethodDelegate3'
IL_003b: pop
IL_003c: ret
Ответ 2
На мой взгляд, нет никакой разницы.
new Action(() => DoNothing(param));
Это просто создает новое действие и проходит по выражению Lambda, с которым компилятор справится и будет следить за тем, чтобы все было подключено просто отлично.
(Action)(() => DoNothing(param));
Это работает, потому что метод лямбда, такой как это, не возвращает никакого значения и не принимает никаких параметров, поэтому компилятор может проверить, что он "отображается" в Action на том основании, что он проходит через систему делегатов.
Они более или менее одно и то же, в зависимости от каких-либо оптимизаций компилятора, трудно сказать, что более эффективно, возможно, вы должны проверить производительность и убедиться сами?
Это интересный вопрос и исследование в системе делегирования и то, как Linq и Expressions подходят.
new Func<string>(() => "Boo!");
более или менее эквивалентно:
(Func<String>)() => "Boo!";
Насколько мне известно, они оба опускаются через систему делегатов, думают Invoke()
т.д., Было бы интересно, если бы вы проверили производительность и поделились своими результатами.
Ответ 3
Там нет разницы, для них это всего два синтаксиса. Лично я использую последний, потому что он короче.
Но зачем нужна переменная типа Delegate
? В большинстве случаев вы хотите, чтобы переменная имела тот же тип, что и экземпляр, а затем вы можете использовать
var bar = (Action)(() => DoNothing(param));
или
Action bar = () => DoNothing(param);
вместо
Delegate bar = (Action)(() => DoNothing(param)); // (from your question)