匿名委托似乎并没有强制类型检查

问题描述:

我已经把下面的一个小代码样本(目前在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#中有很多事情,named-delegates,anonymous delegates和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")); 

不,这加强了该类型检查。如果在分配给代理人时没有为匿名函数提供参数,它将采用默认值。

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

匿名方法表达式或 λ-表达被分类为 匿名函数(§7.14)。 表达式不具有类型,但 可以隐式转换为 兼容委托类型或表达式 树类型。具体而言,一个委托 类型d与匿名 函数F提供兼容:

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

如果您编译源代码&打开它反射(在Framework 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"); 
} 
+0

这真的很有启发性,谢谢 – Steve 2010-02-23 08:18:09

+0

@steve,很高兴它帮助... :) – RameshVel 2010-02-23 08:53:52

当使用匿名方法,什么是真正发生的是一个类别与每个定义的属性创建的时候要稍微明显代表的参数。

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

+0

我已经忘了有多危险匿名委托可能是...你是否同意,从编码的角度看,它会为了避免意外的“错误输入”,使用等效的lambda表达式(按照编辑#1) – 2010-02-23 01:33:48

+0

我不明白你怎么能得到类型安全问题,如果你使用不同类型的参数代码不会编译。 lambda语法只是一个偏好问题,我认为更短,我更喜欢这个,但我不能看到有关错误打字的好处 – 2010-02-23 01:54:11

如果不指定与delegate关键字创建匿名方法paramters,参数是由编译器自动推断的,所以没关系的委托签名是什么