Разница между неявным и явным созданием делегата (с и без дженериков) - PullRequest
5 голосов
/ 14 мая 2009

См. Четыре строки в методе Go () ниже:

delegate void Action<T>(T arg);
delegate void Action();

void DoSomething<T>(Action<T> action)
{
    //...
}

void DoSomething(Action action)
{
    //...
}

void MyAction<T>(T arg)
{
    //...
}

void MyAction()
{
    //...
}

void Go<T>()
{
    DoSomething<T>(MyAction<T>); // throws compiler error - why?
    DoSomething(new Action<T>(MyAction<T>)); // no problems here
    DoSomething(MyAction); // what's the difference between this...
    DoSomething(new Action(MyAction)); // ... and this?
}

Обратите внимание, что ошибка компилятора, сгенерированная при первом вызове: Аргументы типа для метода 'Action (T)' не могут быть выведены из использования. Попробуйте указать аргументы типа явно.

Ответы [ 3 ]

14 голосов
/ 14 мая 2009

Нет разницы между MyAction и new Action(MyAction) (когда они оба действительны), кроме первого, не будет работать в C # 1. Это implicit method group conversion. Временами это не применимо, особенно когда компилятор не может определить, какой тип делегата вам нужен, например,

Delegate foo = new Action(MyAction); // Fine
Delegate bar = MyAction; // Nope, can't tell target type

Это входит в игру в вашем вопросе, потому что оба метода перегружены. В основном это приводит к головным болям.

Что касается дженериков - это интересно. Группы методов не получают большой любви от вывода типа C # 3 - я не уверен, будет ли это улучшено в C # 4 или нет. Если вы вызываете универсальный метод и указываете аргумент типа, вывод типа работает довольно хорошо - но если вы попытаетесь сделать это наоборот, он потерпит неудачу:

using System;

class Test
{
    static void Main()
    {
        // Valid - it infers Foo<int>
        DoSomething<int>(Foo);
        // Valid - both are specified
        DoSomething<int>(Foo<int>);
        // Invalid - type inference fails
        DoSomething(Foo<int>);
        // Invalid - mismatched types, basically
        DoSomething<int>(Foo<string>);
    }

    static void Foo<T>(T input)
    {
    }

    static void DoSomething<T>(Action<T> action)
    {
        Console.WriteLine(typeof(T));
    }
}

Вывод типа в C # 3 очень сложен и хорошо работает в большинстве случаев (в частности, он отлично подходит для LINQ), но не работает в некоторых других. В идеальном мире стало бы легче понимать и более мощные в будущих версиях ... посмотрим!

3 голосов
/ 14 мая 2009

Создание неуниверсального неявного делегата является просто синтаксическим сахаром, поэтому компилятор генерирует точно такой же код для

DoSomething(MyAction);

и

DoSomething(new Action(MyAction));

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

При использовании универсального делегата вы должны указать тип делегата из-за ковариации и контравариантности (см. http://msdn.microsoft.com/en-us/library/ms173174(VS.80).aspx) - T в действии может быть супертипом к T в методе, и он будет все еще быть принятым в качестве метода делегата. Таким образом, вам нужно явно указать T в делегате, так как компилятор не может понять это сам.

1 голос
/ 15 февраля 2017

Просто знакомая .. По какой-то причине это работает в VB.

Кажется, реализация препроцессора в C # и VB отличается, когда я перехожу к преобразованию Methodgroup / adderessof в делегат.

...