2010-06-13 57 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