4

我在下面汇总了一个小代码示例(目前在 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:使用 Lambda 方法

 //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# 中的许多东西一样,命名委托、匿名委托和 lambda 方法各有各的位置,在“代码可读性”、“代码编译器扩展”和个人适用性之间存在平衡正在编写的应用程序。

下面的回复帮助回答了这个问题,并表明编译器确实在做类似于以下的事情。

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 - 但是,当重写为 lambda 表达式时,稍后(或向其他开发人员)查看代码时会稍微明显一些

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

3 回答 3

3

不,这会强制进行类型检查。如果在分配给委托时未为匿名函数提供参数,它将采用默认值。

请参阅 C# 语言规范(§6.5),它指出

匿名方法表达式或 lambda 表达式被归类为匿名函数(第 7.14 节)。表达式没有类型,但可以隐式转换为兼容的委托类型或表达式树类型。具体来说,委托类型 D 与提供的匿名函数 F 兼容:

  • 如果 F 包含匿名函数签名,则 D 和 F 具有相同数量的参数。
  • 如果 F 不包含匿名函数签名,则 D 可以有零个或多个任何类型的参数,只要 D 的任何参数都没有 out 参数修饰符。

如果您编译源代码并在Reflector中打开它(在框架 1.1 设置中),您会看到编译器自动将默认参数分配给没有参数列表的匿名方法。

 del_TestWithNoParams d4_nocompile = (CS$<>9__CachedAnonymousMethodDelegate40 != null) ? CS$<>9__CachedAnonymousMethodDelegate40 : (CS$<>9__CachedAnonymousMethodDelegate40 = new del_TestWithNoParams(Program.<Main>b__27));
    del_TestWithThreeInts d5_nocompile = (CS$<>9__CachedAnonymousMethodDelegate41 != null) ? CS$<>9__CachedAnonymousMethodDelegate41 : (CS$<>9__CachedAnonymousMethodDelegate41 = new del_TestWithThreeInts(Program.<Main>b__28));
    del_TestWithIntPtr d6_nocompile = (CS$<>9__CachedAnonymousMethodDelegate42 != null) ? CS$<>9__CachedAnonymousMethodDelegate42 : (CS$<>9__CachedAnonymousMethodDelegate42 = new del_TestWithIntPtr(Program.<Main>b__29));

而 b__28(委托 del_TestWithThreeInts 的方法)将是这样的

[CompilerGenerated]
private static void <Main>b__28(int, int, int)
{
    Console.WriteLine("AnonymousDel d5");
}
于 2010-02-23T07:42:00.547 回答
1

当使用匿名方法时,真正发生的是创建了一个类,该类具有为委托的每个参数定义的属性。

如果您不传递参数值,则使用默认值。

于 2010-02-23T01:11:55.633 回答
0

如果您不为使用delegate关键字创建的匿名方法指定参数,则编译器会自动推断参数,因此委托签名是什么并不重要

于 2010-02-23T01:12:18.573 回答