2010-06-13 113 views
33

可能重複:
Is it safe for structs to implement interfaces?結構,接口和拳擊

把這個代碼:

interface ISomeInterface 
{ 
    public int SomeProperty { get; } 
} 

struct SomeStruct : ISomeInterface 
{ 
    int someValue; 

    public int SomeProperty { get { return someValue; } } 

    public SomeStruct(int value) 
    { 
     someValue = value; 
    } 
} 

,然後我做到這一點的地方:

ISomeInterface someVariable = new SomeStruct(2); 

SomeStruct盒裝在這種情況下?

回答

45

是的。基本上每當你需要一個參考,你只有一個值類型的值,該值被裝箱。

這裏,ISomeInterface是一個接口,它是一個引用類型。因此,someVariable的值始終是一個引用,所以新創建的結構值必須裝箱。

+0

我假設。不完全確定是什麼讓我懷疑會是這樣。只是以爲我會把它扔出去,以防萬一別人有奇怪的想法。 – Sekhat 2010-06-13 15:51:46

+2

給男人一個工具來獲得答案(紅門反射器),他會有生命的答案。但給他一個答案,他會回來更多的問題和更多的SO重點... – 2010-06-13 17:24:27

+18

@Ben:另一方面,給男人一個工具,他們將不得不每次檢查它,不確定。給一個男人一個解釋*,他們可以爲自己推理一下。 – 2010-06-13 17:39:04

0

MSDN documentation告訴我們結構是價值,而不是參考類型。當轉換爲object類型的變量時,它們被裝箱。但是這裏的核心問題是:接口類型的變量呢?既然接口也可以通過一個類來實現,那麼這肯定等於從一個值轉換爲一個引用類型,就像Jon Skeet已經說過的那樣,因此是會發生裝箱的。 More discussion on an msdn blog

+0

想想這個問題最簡單的方法是認識到除了(可能是空的)接口組合以外,每個變量,參數或字段都需要有一些具體的分配類型。如果沒有其他具體類型可用,則系統將採用對象引用。 – supercat 2011-12-23 23:13:58

54

喬恩的觀點是真實的,但作爲一個便箋,這個規則有一個輕微的例外;仿製藥。如果您有where T : ISomeInterface,那麼這是約束,並使用special opcode。這意味着接口可以用而不用裝箱。例如:

public static void Foo<T>(T obj) where T : ISomeInterface { 
    obj.Bar(); // Bar defined on ISomeInterface 
} 

這並不包括拳擊,即使是價值型T。但是,如果(在同一Foo)你這樣做:

ISomeInterface asInterface = obj; 
asInterface.Bar(); 

那麼這箱前。 約束只有直接適用於T

+0

海,它不會被裝箱,因爲所有的泛型被解析後調用的方法是'void Foo(SomeStruct obj)'不'void Foo(ISomeInterface obj)' – Sekhat 2010-06-13 18:52:56

+1

@Sekhat:泛型參數在運行時被解析,所以編譯器不知道用值類型調用該方法。 – adrianm 2010-06-13 19:16:33

+1

@Sekhat - 擴展@ adrianm的觀點:所有呼叫者都使用相同的IL。每個值類型參數都單獨被打印,但所有參考類型共享一個JIT。編譯器有**沒有**與此做; .NET泛型是運行時,而不是編譯時。每種情況下簽名都是Foo(T obj)。 – 2010-06-13 19:18:54

8

我加了這個,希望能夠多一點 light由Jon和Marc提供的答案。

考慮這種非泛型方法:

public static void SetToNull(ref ISomeInterface obj) { 
    obj = null; 
} 

嗯...設置ref參數設置爲null。這隻適用於參考類型,對嗎? (嗯,或者對於Nullable<T>;但是讓我們忽略這種情況以使事情變得簡單)。因此,此方法編譯的事實告訴我們,聲明爲某種接口類型的變量必須被視爲引用類型。

關鍵短語這裏「聲明」:考慮這個試圖調用上述方法:

var x = new SomeStruct(); 

// This line does not compile: 
// "Cannot convert from ref SomeStruct to ref ISomeInterface" -- 
// since x is declared to be of type SomeStruct, it cannot be passed 
// to a method that wants a parameter of type ref ISomeInterface. 
SetToNull(ref x); 

當然,你不能在上面的代碼通過xSetToNull原因是x會需要聲明爲ISomeInterface您能夠通過ref x而不是,因爲編譯器神奇地知道SetToNull包含行obj = null。但是,僅僅加強我的觀點的方式:在obj = null線是合法正是因爲這將是非法傳遞變量聲明爲ISomeInterface的方法。

換句話說,如果一個變量聲明爲ISomeInterface,它可以設置爲空,純粹和簡單。這是因爲接口是引用類型 - 因此,將對象聲明爲接口並將其分配給值類型的值對象框。

現在,在另一方面,考慮這個假設的泛型方法:

// This method does not compile: 
// "Cannot convert null to type parameter 'T' because it could be 
// a non-nullable value type. Consider using 'default(T)' instead." -- 
// since this method could take a variable declared as, e.g., a SomeStruct, 
// the compiler cannot assume a null assignment is legal. 
public static void SetToNull<T>(ref T obj) where T : ISomeInterface { 
    obj = null; 
} 
+1

這與價值類型和參考類型以及與變化無關的一切無關。 – 2010-06-13 20:31:09

+4

@Ben:我猜你是在說因爲我的「ref」例子,我猶豫了,因爲我認爲它可能有點混亂。但我的觀點是,如果一個變量被聲明爲一個'ISomeInterface',它可以被設置爲null,這僅僅是一個引用類型。因此,將一個「ISomeInterface」變量設置爲值類型的對象會導致裝箱。這確實與值類型和引用類型有很大關係。如果一個變量被聲明爲特定的值類型,那麼該變量*不能被設置爲null。 – 2010-06-13 21:04:19