2010-02-23 39 views
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#中有很多事情,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")); 

回答

3

不,這加強了該類型檢查。如果在分配給代理人時沒有爲匿名函數提供參數,它將採用默認值。

參見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

1

當使用匿名方法,什麼是真正發生的是一個類別與每個定義的屬性創建的時候要稍微明顯代表的參數。

如果您未傳遞參數值,則使用默認值。

+0

我已經忘了有多危險匿名委託可能是...你是否同意,從編碼的角度看,它會爲了避免意外的「錯誤輸入」,使用等效的lambda表達式(按照編輯#1) – 2010-02-23 01:33:48

+0

我不明白你怎麼能得到類型安全問題,如果你使用不同類型的參數代碼不會編譯。 lambda語法只是一個偏好問題,我認爲更短,我更喜歡這個,但我不能看到有關錯誤打字的好處 – 2010-02-23 01:54:11

0

如果不指定與delegate關鍵字創建匿名方法paramters,參數是由編譯器自動推斷的,所以沒關係的委託簽名是什麼