[英]Anonymous delegates don't seem to enforce type-checking
我在下面匯總了一個小代碼示例(目前在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"));
不,這會強制進行類型檢查。 如果在分配給委托時沒有為匿名函數提供參數,則它將采用默認值。
它指出了C#語言規范(§6.5)
anonymous-method-expression或lambda-expression被歸類為匿名函數(第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");
}
當使用匿名方法時,實際發生的是創建一個類,其中為委托的每個參數定義了一個屬性。
如果未傳遞參數值,則使用默認值。
如果沒有為使用delegate
關鍵字創建的匿名方法指定參數,則編譯器會自動推斷出參數,因此委托簽名是什么並不重要
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.