结构,接口和拳击

时间:2010-06-13 15:26:26

标签: c# struct interface value-type boxing

  

可能重复:
  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

4 个答案:

答案 0 :(得分:62)

乔恩的观点是正确的,但作为旁注,该规则有一个轻微的例外;仿制药。如果您有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

答案 1 :(得分:48)

是的,确实如此。基本上,只要您需要引用并且您只获得了值类型值,该值就会被加框。

这里,ISomeInterface是一个接口,它是一个引用类型。因此someVariable的值始终是引用,因此必须将新创建的struct值装箱。

答案 2 :(得分:8)

我正在添加这个,希望能够更多地了解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; } ,则可以将其设置为null,纯粹且简单。这是因为接口是引用类型 - 因此,将对象声明为接口并将其分配给值类型的值对象框。

另一方面,现在考虑这个假设的通用方法:

{{1}}

答案 3 :(得分:0)

MSDN documentation告诉我们结构是值,而不是引用类型。转换为/ object类型的变量时,它们会被装箱。但这里的核心问题是:接口类型的变量怎么样?由于接口也可以由类实现,因此这必须等同于从值转换为引用类型,正如Jon Skeet已经说过的那样,因此会发生拳击。 More discussion on an msdn blog