2009-12-09 45 views
6

使用的lambda表達式,我們正在建設我們需要下面的模式框架:角的情況下,在基類的構造

public class BaseRenderer 
{ 
    Func<string> renderer; 
    public BaseRenderer(Func<string> renderer) 
    { 
     this.renderer = renderer; 
    } 

    public string Render() 
    { 
     return renderer(); 
    } 
} 

public class NameRenderer : BaseRenderer 
{ 
    public string Name{ get; set; } 

    public NameRenderer() 
     : base(() =>this.Name) 
    {} 
} 

正如你看到的,我們調用基類的構造時創建拉姆達。

public class Program 
{ 
    public static void Main() 
    { 
     Console.WriteLine(new NameRenderer(){Name = "Foo"}.Render()); 
    } 
} 

奇怪的是,試圖真正使用它拋出NullReferenceException異常(控制檯應用程序)的拉姆達,或某種ExecutionEngineExceptionexception的(在IIS的Web應用程序)時。

我認爲原因是這個指針在調用基構造器之前還沒有準備好,所以lambda在這個階段無法捕獲this.Name

它不應該在「捕獲時間」而不是「執行時間」中拋出異常嗎? 此行爲是否記錄在案?

我可以以不同的方式重構代碼,但我認爲它值得評論。

+2

這是之前問過的,它會在C#4.0中修復的 – leppie 2009-12-09 19:36:41

回答

22

正如asgerhallas正確指出的那樣,根據規範,這應該是不合法的。我們意外地允許這種虛假使用方式被錯誤檢測器潛入,該錯誤檢測器在合法的情況下搜索「this」的不正確用法。我修復了這個錯誤; C#4編譯器會正確地將您的程序標記爲錯誤。

很多道歉造成的不便,這是我的錯誤。

+6

hah,並不常見a)你在編譯器中遇到了一個錯誤,並且b)負責的開發人員用一個mea culpa來回應你的問題:-P sweet! – 2009-12-09 19:37:43

+1

C#團隊非常棒,謝謝Eric。 – Olmo 2009-12-10 08:15:14

4

我認爲你是對的。當調用基類構造函數時,子類尚未構造,因此訪問子類上的成員會爲您提供空引用。 CLR沒有辦法在編譯時知道實例是否存在。

將邏輯移動到構造函數主體應該可以解決問題。

6

7.5.7中的C#規範指出:「僅在實例構造函數,實例方法或實例訪問器的塊中允許訪問此訪問權限。」

甚至更​​直接地在10.11.1中:「一個實例構造函數初始化程序不能訪問正在創建的實例,因此,在構造函數初始化程序的一個參數表達式中引用它是一個編譯時錯誤,就像它是一個編譯參數表達式通過簡單名稱引用任何實例成員的時間錯誤。「

雖然實例已根據7.5.10創建。

嗯。這實際上很奇怪。我沒有看到任何編譯時錯誤。

+0

我第一次不太對。當時有一個實例,但您無法訪問它。答案已更新。 – asgerhallas 2009-12-09 19:21:59

+5

您沒有看到編譯錯誤,因爲編譯器有錯誤。它應該是一個錯誤,在C#4中是這樣。 – 2009-12-09 19:31:01

2

該lambda捕獲「this」的值並捕獲null,因爲該對象尚未構造。這讓我覺得它是一個編譯器錯誤,它應該爲此產生一個錯誤。這樣的代碼通常會生成CS0027(關鍵字'this'在當前上下文中不可用)或CS0120(需要對象引用)。我敢打賭,這並不容易實現。

Anyhoo,代碼無法正常工作。 NameRenderer類需要一個帶有字符串參數的構造函數,以便它可以初始化基類。

1

不會: base(()=>this)合法嗎?你可以做: this(),所以對此的引用似乎很好,只是沒有它的屬性。: base(()=>this)不再合法的事實只是打破了我在施工過程中所做的部分功能應用。可以通過將它移動到構造函數的主體中來解決,但是存在順序差異:基類不能再透明地傳遞給它自己的部分函數應用程序(因爲基類構造函數在子類構造函數的主體之前被調用)。

相關問題