我在下面放了一个小代码示例(目前在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,匿名委托和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"));
答案 0 :(得分:3)
不,这会强制进行类型检查。如果在分配给委托时没有为匿名函数提供参数,则它将采用默认值。
参考C#语言规范(第6.5节),它陈述
anonymous-method-expression或 lambda-expression被归类为 匿名函数(第7.14节)。该 表达式没有类型但是 可以隐式转换为 兼容的委托类型或表达式 树型。具体来说,是代表 类型D与匿名兼容 功能F提供:
- 如果F包含匿名函数签名,则为D. 和F的数量相同 参数。
- 如果F不包含匿名函数签名,则为D. 可能有零个或多个参数 任何类型,只要没有D的参数 有out参数修饰符。
如果你编译你的源代码&在 Reflector (在框架1.1设置中)中打开它,您将看到编译器自动将默认参数分配给没有param列表的Anonymous方法。
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");
}
答案 1 :(得分:1)
当使用匿名方法时,真正发生的是创建一个类,其中为该委托的每个参数定义了一个属性。
如果未传递参数值,则使用默认值。
答案 2 :(得分:0)
如果没有为使用delegate
关键字创建的匿名方法指定参数,则编译器会自动推断出参数,因此委托签名是什么并不重要