Как работают делегирование / лямбда-типинг и принуждение?

Я заметил несколько примеров того, что работает и не работает при работе с лямбда-функциями и анонимными делегатами в C#. Что тут происходит?

class Test : Control {
    void testInvoke() {
        // The best overloaded method match for 'Invoke' has some invalid arguments
        Invoke(doSomething);

        // Cannot convert anonymous method to type 'System.Delegate' because it is not a delegate type
        Invoke(delegate { doSomething(); });

        // OK
        Invoke((Action)doSomething);

        // OK
        Invoke((Action)delegate { doSomething(); });

        // Cannot convert lambda expression to type 'System.Delegate' because it is not a delegate type
        Invoke(() => doSomething());

        // OK
        Invoke((Action)(() => doSomething()));
    }

    void testQueueUserWorkItem() {
        // The best overloaded method match for 'QueueUserWorkItem' has some invalid arguments
        ThreadPool.QueueUserWorkItem(doSomething);

        // OK
        ThreadPool.QueueUserWorkItem(delegate { doSomething(); });

        // The best overloaded method match for 'QueueUserWorkItem' has some invalid arguments
        ThreadPool.QueueUserWorkItem((Action)doSomething);

        // No overload for 'doSomething' matches delegate 'WaitCallback'
        ThreadPool.QueueUserWorkItem((WaitCallback)doSomething);

        // OK
        ThreadPool.QueueUserWorkItem((WaitCallback)delegate { doSomething(); });

        // Delegate 'WaitCallback' does not take '0' arguments
        ThreadPool.QueueUserWorkItem(() => doSomething());

        // OK
        ThreadPool.QueueUserWorkItem(state => doSomething());
    }

    void doSomething() {
        // ...
    }
}

Ну, это много примеров. Я думаю, мои вопросы следующие:

  1. Почему Invoke всегда отказываются от лямбда-функции или анонимного делегата, но ThreadPool.QueueUserWorkItem просто отлично?

  2. Что, черт возьми, означает "Невозможно преобразовать анонимный метод в тип" System.Delegate ", потому что это не тип делегата"?

  3. Почему ThreadPool.QueueUserWorkItem принять анонимный делегат без параметров, но не лямбда-выражение без параметров?

1 ответ

Решение
  1. ThreadPool.QueueUserWorkItem имеет конкретного делегата в своей подписи; Вызвать только что Delegate, Лямбда-выражения и анонимные методы могут быть преобразованы только в определенный тип делегата.

  2. Это просто плохое сообщение об ошибке. Это означает: "Я не знаю точно, к какому типу делегата вы пытаетесь преобразовать".

  3. Вы используете анонимный метод без списка параметров, который можно преобразовать в любой тип делегата, который не использует параметры out/ref. Если вы пытались delegate() { ... } (то есть явно пустой список параметров), тогда это не будет работать. Эта способность анонимных методов "мне наплевать на параметры" - единственная функция, которой обладают лямбда-выражения.

Проще всего продемонстрировать все это в контексте простых заданий, IMO:

// Doesn't work: no specific type
Delegate d = () => Console.WriteLine("Bang");

// Fine: we know the exact type to convert to
Action a = () => Console.WriteLine("Yay");

// Doesn't work: EventHandler isn't parameterless; we've specified 0 parameters
EventHandler e1 = () => Console.WriteLine("Bang");
EventHandler e2 = delegate() { Console.WriteLine("Bang again"); };

// Works: we don't care about parameter lists
EventHandler e = delegate { Console.WriteLine("Lambdas can't do this"); };
Другие вопросы по тегам