结构,接口和拳击
可能重复:
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
盒装在这种情况下?
是的。基本上每当你需要一个参考,你只有一个值类型的值,该值被装箱。
这里,ISomeInterface
是一个接口,它是一个引用类型。因此,someVariable
的值始终是一个引用,所以新创建的结构值必须装箱。
MSDN documentation告诉我们结构是价值,而不是参考类型。当转换为object
类型的变量时,它们被装箱。但是这里的核心问题是:接口类型的变量呢?既然接口也可以通过一个类来实现,那么这肯定等于从一个值转换为一个引用类型,就像Jon Skeet已经说过的那样,因此是会发生装箱的。 More discussion on an msdn blog。
想想这个问题最简单的方法是认识到除了(可能是空的)接口组合以外,每个变量,参数或字段都需要有一些具体的分配类型。如果没有其他具体类型可用,则系统将采用对象引用。 – supercat 2011-12-23 23:13:58
乔恩的观点是真实的,但作为一个便笺,这个规则有一个轻微的例外;仿制药。如果您有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
。
海,它不会被装箱,因为所有的泛型被解析后调用的方法是'void Foo(SomeStruct obj)'不'void Foo(ISomeInterface obj)' – Sekhat 2010-06-13 18:52:56
@Sekhat:泛型参数在运行时被解析,所以编译器不知道用值类型调用该方法。 – adrianm 2010-06-13 19:16:33
@Sekhat - 扩展@ adrianm的观点:所有呼叫者都使用相同的IL。每个值类型参数都单独被打印,但所有参考类型共享一个JIT。编译器有**没有**与此做; .NET泛型是运行时,而不是编译时。每种情况下签名都是Foo(T obj)。 – 2010-06-13 19:18:54
我加了这个,希望能够多一点 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);
当然,你不能在上面的代码通过x
到SetToNull
原因是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;
}
这与价值类型和参考类型以及与变化无关的一切无关。 – 2010-06-13 20:31:09
@Ben:我猜你是在说因为我的“ref”例子,我犹豫了,因为我认为它可能有点混乱。但我的观点是,如果一个变量被声明为一个'ISomeInterface',它可以被设置为null,这仅仅是一个引用类型。因此,将一个“ISomeInterface”变量设置为值类型的对象会导致装箱。这确实与值类型和引用类型有很大关系。如果一个变量被声明为特定的值类型,那么该变量*不能被设置为null。 – 2010-06-13 21:04:19
我假设。不完全确定是什么让我怀疑会是这样。只是以为我会把它扔出去,以防万一别人有奇怪的想法。 – Sekhat 2010-06-13 15:51:46
给男人一个工具来获得答案(红门反射器),他会有生命的答案。但给他一个答案,他会回来更多的问题和更多的SO重点... – 2010-06-13 17:24:27
@Ben:另一方面,给男人一个工具,他们将不得不每次检查它,不确定。给一个男人一个解释*,他们可以为自己推理一下。 – 2010-06-13 17:39:04