Похоже, что анонимные делегаты не проводят проверку типов - PullRequest
4 голосов
/ 23 февраля 2010

Ниже я собрал небольшой пример кода (в настоящее время в C # 3.5, но также хотел бы знать, отличается ли ответ в C # 4.0)

У меня есть три простых делегата и три простые функции ... Здесь нет проблем, все компилируется, как ожидается, и не будет компилироваться, если я случайно попытаюсь связать делегата A с методом B и т. Д. (Неверное количество параметров).

Я пытаюсь понять, почему анонимные функции кажутся счастливыми быть связанными со всеми тремя именованными делегатами

public class Foo
{
   public delegate void del_TestWithNoParams();
   public delegate void del_TestWithThreeInts(int x, int y, int z);
   public delegate void del_TestWithIntPtr(IntPtr ptr);

   public void DoStuff()
   {
      //All OK so Far ... Code will not compile if I mix these up
      del_TestWithNoParams d1 =
         TestWithNoParams; d1();
      del_TestWithThreeInts d2 =
         TestWithThreeInts; d2(2, 4, 8);
      del_TestWithIntPtr d3 =
         TestWithIntPtr; d3(new IntPtr(0x1234567));

      //Why do these compile
      del_TestWithNoParams d4_nocompile =
         delegate { Console.WriteLine("AnonymousDel d4"); };
      del_TestWithThreeInts d5_nocompile =
         delegate { Console.WriteLine("AnonymousDel d5"); };
      del_TestWithIntPtr d6_nocompile =
         delegate { Console.WriteLine("AnonymousDel d6"); };

      // Edit 1 goes here
   }

     public void TestWithNoParams()
     { Console.WriteLine("NoParams"); }
     public void TestWithThreeInts(int x, int y, int z)
     { Console.WriteLine("Ints: {0},{1},{2}", x, y, z); }
     public void TestWithIntPtr(IntPtr ptr)
     { Console.WriteLine("IntPtr: 0x{0:X8}", ptr.ToInt32()); }

 }

Также (просто чтобы дать вам полное работоспособное приложение ...)

static void Main(string[] args)
  {
     var f = new Foo();
     f.DoStuff();
     Console.WriteLine("Done"); Console.ReadLine();
  }

Редактировать 1: Использование лямбда-методов

 //This work as expected - and fail to build if I get the parameter-count wrong.
 del_TestWithNoParams d7 =
   (() => Console.WriteLine("Lambda NoParams"));
 del_TestWithThreeInts d8 =
   ((a, b, c) => Console.WriteLine("Lambda Ints: {0},{1},{2}", a, b, c));
 del_TestWithIntPtr d9 =
   ((ptr) => Console.WriteLine("Lambda IntPtr: 0x{0:X8}", ptr.ToInt32()));
 Test(d7, d8, d9);

Простая вспомогательная функция:

private void Test(del_TestWithNoParams del_A, del_TestWithThreeInts del_B, del_TestWithIntPtr del_C)
{
   del_A();
   del_B(2, 4, 8);
   del_C(new IntPtr(0x1234567));
}

... Согласитесь ли вы, что это лучший метод для написания того же кода ???


Редактировать # 2 - Сводка ответов

Я понимаю, что (каким бы образом я ни писал код), сгенерированный байт-код IL по-прежнему является типобезопасным.

Как и во многих вещах в C #, именованные делегаты, анонимные делегаты и лямбда-методы имеют свое место, и существует баланс между "читаемостью кода", "расширением кода компилятора" и пригодностью для написания индивидуальной заявки.

Ответы ниже помогли ответить на вопрос и показать, что компилятор действительно делает что-то похожее на следующее.

1 - Это НЕ позволит мне сделать эту ошибку

//del_TestWithIntPtr d_mistake_A =
//   delegate(int x,int y,int z) { Console.WriteLine(x + y + z); };

2 - «Компилятор, определяющий типы» расширяет делегат (например, d5_nocompile) до

del_TestWithThreeInts d_clearer_3P =
delegate(int x, int y, int z) { Console.WriteLine(x + y + z); };

3 - ВОЗМОЖНО совершить ошибку (все еще действительный код)

del_TestWithThreeInts d_EasyToMakeMistake =
delegate { Console.WriteLine("Oops - forgot to do anything with params"); };
// (this is really :- delegate (int x, int y, int z) {...} )

4 - Однако, когда перезаписывается как лямбда-выражение, это становится немного более очевидным при просмотре кода позже (или другому разработчику)

del_TestWithThreeInts d_LessEasyToMakeMistake =
((x, y, z) => Console.WriteLine("Still POSSIBLE to make mistake, but more obvious"));

Ответы [ 3 ]

3 голосов
/ 23 февраля 2010

Нет, это принудительно проверяет тип.Принимает значения по умолчанию, если при назначении делегата параметры не указываются для анонимной функции.

См. Спецификацию языка C # (§6.5), там говорится:

Выражение анонимного метода или лямбда-выражения классифицируется как анонимная функция (§7.14).Выражение не имеет типа, но может быть неявно преобразовано в совместимый тип делегата или тип дерева выражений.В частности, тип делегата D совместим с анонимной функцией F при условии:

  • Если F содержит сигнатуру анонимной функции, то D и F имеют одинаковое количество параметров.
  • Если F не содержит сигнатуру анонимной функции, то D может иметь ноль или более параметров любого типа, если ни один из параметров D не имеет модификатора out-параметра.

Если вы скомпилируете свой исходный код и откроете его в Reflector (в настройке framework 1.1), вы увидите, что компилятор автоматически назначает параметры по умолчанию для анонимных методов, у которых нет списка параметров.

И b__28 (метод для делегата del_TestWithThreeInts) будет выглядеть примерно так

[CompilerGenerated]
private static void <Main>b__28(int, int, int)
{
    Console.WriteLine("AnonymousDel d5");
}
1 голос
/ 23 февраля 2010

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

Если вы не передаете значения параметров, используются значения по умолчанию.

0 голосов
/ 23 февраля 2010

Если вы не укажете параметры для анонимных методов, созданных с помощью ключевого слова delegate, компилятор автоматически определит параметры, поэтому не имеет значения, какая подпись делегата

...