2010-09-20 31 views
9

我有這個C#DLL:F#/。NET空實例古怪

namespace TestCSProject 
{ 
    public class TestClass 
    { 
     public static TestClass Instance = null; 

     public int Add(int a, int b) 
     { 
      if (this == null) 
       Console.WriteLine("this is null"); 
      return a + b; 
     } 
    } 
} 

和引用DLL這個F#應用程序:

open TestCSProject 
printfn "%d" (TestClass.Instance.Add(10,20)) 

沒有人發起Instance靜態變量。猜猜F#應用程序的輸出是什麼?

 
this is null 
30 
Press any key to continue . . . 

幾個測試後,我發現,除非我用this(例如訪問實例字段),我不會得到NullReferenceExpcetion。

這是F#編譯/ CLR中的預期行爲還是缺口?

+0

+1:哦哇,這是我以前沒見過的東西:) – Juliet 2010-09-20 16:05:40

回答

10

我懷疑你會發現它使用call而不是callvirt如果你看看IL。 C#編譯器始終使用callvirt,即使對於非虛方法,因爲它強制進行無效檢查。

這是一個錯誤?嗯,不一定。它取決於F#語言規範在空引用上對方法調用的說明。它完全有可能表示該方法將被調用(非虛擬),並帶有一個空的「this」引用,這正是發生的事情。

C#恰巧指定這種解除引用會拋出NullReferenceException,但這是一種語言選擇。

我懷疑F#方法可能會更快一些,因爲缺乏無效性檢查......並且不要忘記,F#中的空引用比C#中的「預計」更少...... 可能解釋了這裏採取的不同方法。或者當然,這可能只是一個疏忽。

編輯:我不是在閱讀F#規格方面的專家,但第6.9.6至少建議,我認爲這是一個錯誤:

6.9.6評價方法應用

對於方法的詳細應用,表達式的詳細表達式將是 expr.M(args)或M(args)。

  • (可選的)EXPR和args在左到右的順序計算和構件的主體上並與被映射到相應的參數值的形式參數的環境下進行評價。

  • 如果expr的計算結果爲null,則引發NullReferenceException。

  • 如果該方法是一個虛擬調度槽(即聲明爲抽象的方法),則根據expr值的調度映射選擇該成員的主體。

這是否算作一個詳細的應用程序或不有點超越我,我怕......但我希望這已經至少在某種程度上幫助。

+0

你是對的,你的解釋聽起來很合理。生成了不同的MSIL指令。我不知道可以用非虛擬的方式在.NET中調用方法。感謝Jon – Elephantik 2010-09-20 15:29:16

+0

@Elephantik:有趣的是,你可以在IL中使用'call'來非虛擬地調用虛擬方法,打破封裝。 (這是C#如何調用'base.Foo()'在內部工作。) – 2010-09-20 15:30:16

+1

確實。另見http://blogs.msdn.com/b/ericlippert/archive/2010/03/29/putting-a-base-in-the-middle.aspx,瞭解一些特別醜陋的角落行爲。 – kvb 2010-09-20 15:46:02

3

我認爲F#團隊認爲這種行爲是一個可能在未來版本中發生變化的錯誤。

偶爾,一個類可能會將方法從非虛擬(在版本N中)更改爲虛擬(在版本N + 1中)。這是一個「突變」嗎?如果針對原始類使用call而不是callvirt編譯的代碼,則會中斷。 「突變」的概念大多是而不是;非虛擬到虛擬的變化是一個「次要的」重大變化。在任何情況下,.NET Framework中的某些類都展示了這種變化,因此遇到類似於「脆弱基類」的問題。目睹這一點,F#團隊打算將編譯器代碼更改爲像C#和VB那樣使用callvirt。假設發生了這樣的情況,一些不太可能的程序(例如這個repro中的程序)會在編譯未來版本的F#編譯器時改變行爲。

+0

恕我直言,我發現它有點令人震驚,CLR認爲它有效的MSIL通過使用調用繞過覆蓋,而不是callvirt。我希望看到的是被認爲是有效的MSIL的基本原理。 – 2010-09-21 01:31:23

+0

base.Method()調用如何工作? – Brian 2010-09-21 01:39:11