Почему компилятор не может понять, что эта лямбда является делегатом? - PullRequest
0 голосов
/ 17 мая 2018

Рассмотрим эту крошечную программу.

public class Program
{
    public static void Main()
    {
        // the first path compiles
        RunAction(() => { });

        // the second path does not compile
        RunDelegate(() => {});
    }

    private static void RunAction(Action run) => RunDelegate(run);
    private static void RunDelegate(Delegate run) { }
}

Первый путь компилируется и подразумевает, что

  1. лямбда () => {} является Action,
  2. Action является Delegate, а
  3. для этого () => {} лямбда является Delegate.

Почему второй путь не компилируется?

Обычно компилятор может сделать скачок между шагами (1) и (3). Вот пример того, как компилятор обычно обрабатывает такие вложенные отношения is-a.

public class Program2
{
    public static void Main()
    {
        // both of these comple
        AcceptPerson(new Programmer());
        AcceptAnimal(new Programmer());
    }

    private static void AcceptPerson(Person p) => AcceptAnimal(p);
    private static void AcceptAnimal(Animal a) { }
}

public class Programmer : Person { }
public class Person : Animal { }
public class Animal { }

1 Ответ

0 голосов
/ 17 мая 2018

Не существует типа делегата по умолчанию для анонимной функции, такой как () => { }. Такое выражение неявно преобразуется в любой конкретный тип делегата правильной подписи и возвращаемого типа (с учетом различных естественных правил отклонения).

У вас может быть много типов делегатов, которые соответствуют:

namespace TestFramework
{
  public delegate void TestDelegate();
}

namespace System
{
  public delegate void Action();
}

namespace System.Threading
{
  public delegate void ThreadStart();
}

...

Среди всех таких возможных конкретных типов делегатов (каждый из которых в качестве абстрактного System.Delegate в качестве своего (непрямого) базового класса) не является предпочтительным .

Существует неявное преобразование из выражения () => { } в каждое из них:

TestDelegate f = () => { };
Action g = () => { };
ThreadStart h = () => { };
...

Из-за неявного преобразования ваш вызов RunAction(() => { }); работает.

Но с RunDelegate(() => {}); невозможно сказать, какой конкретный тип делегата использовать. Вы должны сделать одно из:

RunDelegate((TestDelegate)(() => { }));
RunDelegate((Action)(() => { }));
RunDelegate((ThreadStart)(() => { }));
...

Комментарии к вашему вопросу верны.

С этим связан тот факт, что это не скомпилируется:

var f = () => { };  // illegal, shows that '() => { }' does NOT have type System.Action

И вы не можете использовать подвыражение типа myBool ? (() => { }) : null и т. Д.

...