Чому компіляція нормальна, коли я використовую метод Invoke, а не гаразд, коли я повертаю Func <int, int> безпосередньо?


28

Я не розумію цього випадку:

public delegate int test(int i);

public test Success()
{
    Func<int, int> f = x => x;
    return f.Invoke; // <- code successfully compiled 
}

public test Fail()
{
    Func<int, int> f = x => x;
    return f; // <- code doesn't compile
}

Чому компіляція нормальна, коли я використовую Invokeметод, а не гаразд, коли я повертаюся csharp Func<int,int>безпосередньо?


У вас є делегат, що означає, що ви отримуєте якусь подію. Invoke запобігає виключення міжпотокових потоків і дозволяє декільком процесам отримати доступ до об'єкта.
jdweng

Зауважте, що ви побачите цю проблему, навіть якщо ви будете використовувати двох однакових на перший погляд делегатів, таких як delegate void test1(int i);іdelegate void test2(int i);
Меттью Уотсон

Відповіді:


27

Для розуміння такої поведінки потрібно знати дві речі.

  1. Усі делегати походять від System.Delegate, але різні делегати мають різні типи, і тому їх не можна призначати один одному.
  2. Мова C # забезпечує спеціальну обробку для присвоєння методу або лямбда делегату .

Оскільки різні делегати мають різні типи, це означає, що ви не можете призначити делегата одного типу іншому.

Наприклад, наведено:

delegate void test1(int i);
delegate void test2(int i);

Тоді:

test1 a = Console.WriteLine; // Using special delegate initialisation handling.
test2 b = a;                 // Using normal assignment, therefore does not compile.

Перший рядок складається з ОК, оскільки він використовує спеціальну обробку для присвоєння лямбда або методу делегату.

Насправді цей рядок ефективно переписаний таким чином компілятором:

test1 a = new test1(Console.WriteLine);

Другий рядок вище не компілюється, оскільки він намагається призначити екземпляр одного типу іншому несумісному типу.

Наскільки на типи йти, не сумісні між присвоювання test1і test2тому , що вони мають різні типи.

Якщо це допомагає подумати над цим, врахуйте цю ієрархію класів:

class Base
{
}

class Test1 : Base
{
}

class Test2 : Base
{
}

Наступний код НЕ буде компілюватись, хоча Test1і Test2походить з одного базового класу:

Test1 test1 = new Test1();
Test2 test2 = test1; // Compile error.

Це пояснює, чому ви не можете призначити один тип делегата іншому. Це просто звичайна мова C #.

Однак найважливіше - зрозуміти, чому вам дозволяється призначити метод або лямбда сумісному делегату. Як зазначалося вище, це є частиною підтримки мови C # для делегатів.

Отже, нарешті, щоб відповісти на ваше запитання:

Під час використання Invoke()ви призначаєте виклику METHOD делегату, використовуючи спеціальну обробку мови C # для призначення методів або лямбдатів делегату, а не намагатися призначити несумісний тип - значить, він компілює OK.

Щоб бути абсолютно зрозумілим, код, який складається у вашому ОП:

public test Success()
{
    Func<int, int> f = x => x;
    return f.Invoke; // <- code successfully compiled 
}

Насправді концептуально перетворюється на щось на кшталт:

public test Success()
{
    Func<int, int> f = x => x;
    return new test(f.Invoke);
}

Тоді як код, який не працює, намагається призначити між двома несумісними типами:

public test Fail()
{
    Func<int, int> f = x => x;
    return f; // Attempting to assign one delegate type to another: Fails
}

6

У другому випадку fмає тип Func<int, int>, але, як кажуть, метод повернення a test. Це неспоріднені (делеговані) типи, які не можна конвертувати один для одного, тому трапляється помилка компілятора. Ви можете перейти до цього розділу специфікації мови та шукати "делегата". Ви не знайдете згадки про конверсії між делегатами, які мають однакові підписи.

Однак у першому випадку f.Invoke це вираз групи методів , який насправді не має типу. Компілятор C # перетворить вирази групи методів у конкретні типи делегата відповідно до контексту шляхом перетворення групи методів .

(Цитуючи 5-ту кулю тут , наголос мій)

Вираз класифікується як одне з наступних:

  • ...

  • Група методів, що представляє собою набір перевантажених методів, що є результатом пошуку членів. [...] Група методів дозволена у виклику_вираз, делегат_створення_вираз і як ліва частина isоператора, і їх можна неявно перетворити на сумісний тип делегата.

У цьому випадку він перетворюється на test тип делегата.

Іншими словами, return fвін не працює, оскільки fвже має тип, але f.Invokeще не має типу.


2

Випуск тут - сумісність типу:

Далі йде визначення делегата Func від джерел MSDN:

public delegate TResult Func<in T, out TResult>(T arg);

Якщо ви бачите, що між вказаним вище функцією та вашим визначеним делегатом немає прямого зв’язку:

public delegate int test(int i);

Чому перший фрагмент компілює:

public test Success()
{
    Func<int, int> f = x => x;
    return f.Invoke; // <- code successfully compiled 
 }

Делегати порівнюються за допомогою підпису, який є вхідними параметрами та результатом виводу, в кінцевому підсумку Делегат - це вказівник на функцію, і дві функції можна порівняти лише за допомогою підпису. Під час виконання метод, який викликається через Func, присвоюється Testделегату, оскільки Signature такий же, він працює безперешкодно. Це призначення покажчика функції, деTest делегат тепер буде викликати метод, вказаний делегатом Func

Чому 2-й фрагмент не вдається зібрати

Між функцією та тестовим делегатом, немає сумісності типу / призначення, функція не може заповнюватись як частина системних правил типу. Навіть тоді, коли його результат можна призначити та заповнити, test delegateяк це було зроблено у першому випадку.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.