Действие для делегирования: новое действие или действие?

Я нашел два разных способа инициализации делегата с действием:

Создайте новое действие или кастинг для 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)