结构,接口和拳击

Sek*_*hat 43 c# struct boxing interface value-type

可能重复:
结构实现接口是否安全?

拿这个代码:

interface ISomeInterface
{
    public int SomeProperty { get; }
}

struct SomeStruct : ISomeInterface
{
    int someValue;

    public int SomeProperty { get { return someValue; } }

    public SomeStruct(int value)
    {
        someValue = value;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我在某个地方这样做:

ISomeInterface someVariable = new SomeStruct(2);
Run Code Online (Sandbox Code Playgroud)

就是SomeStruct在这种情况下,盒装?

Mar*_*ell 67

乔恩的观点是正确的,但作为旁注,该规则有一个轻微的例外; 仿制药.如果你有where T : ISomeInterface,那么这是受约束的,并使用特殊的操作码.这意味着界面可以在没有装箱的情况下使用.例如:

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

这并没有涉及拳击,即使是价值型T.但是,如果(相同Foo)你这样做:

ISomeInterface asInterface = obj;
asInterface.Bar();
Run Code Online (Sandbox Code Playgroud)

那个盒子和以前一样.该限制 直接应用于T.

  • @Sekhat - 扩展@adrianm 的观点:所有调用者都使用相同的 IL。每个值类型参数都单独进行 JIT,但所有 ref 类型共享一个 JIT。编译器与此**无关**;.NET 泛型是运行时,而不是编译时。在每种情况下,签名都是 Foo(T obj)。 (3认同)
  • @Sekhat:泛型参数在运行时解析,因此编译器不知道使用值类型调用该方法。 (2认同)
  • 我从未说明泛型是在编译时还是运行时解析的。然而,它们在某个时候得到解决。Foo&lt;T&gt; (T obj) 比 Foo(ISomeInterface interface) 更适合 SomeStruct,因为泛型最终被解析为 Foo(SomeStruct obj)。 (2认同)

Jon*_*eet 54

是的.基本上,只要您需要引用并且只获得值类型值,该值就会被装箱.

ISomeInterface是一个接口,它是一个引用类型.因此,值someVariable始终是引用,因此必须将新创建的struct值加框.

  • @Ben:另一方面,给一个男人一个工具,他们每次不确定时都要检查它.给一个人一个*解释*他们将能够为自己推理. (22认同)
  • 给一个人一个获得答案的工具(红门反射器),他将有生命的答案.但只给他一个答案,他会再回来提出更多问题和更多的SO代表...... (2认同)

Dan*_*Tao 9

我正在补充这一点,希望能更多地了解 Jon和Marc提供的答案.

考虑这种非通用方法:

public static void SetToNull(ref ISomeInterface obj) {
    obj = null;
}
Run Code Online (Sandbox Code Playgroud)

嗯...将ref参数设置为null.那只是参考类型,对吗?(好吧,或者对于a 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);
Run Code Online (Sandbox Code Playgroud)

当然,您无法传递x上述代码的原因SetToNullx需要声明ISomeInterface为您能够传递ref x- 而不是因为编译器神奇地知道SetToNull包含该行obj = null.但是在某种程度上只是强调了我的观点:该obj = null行是合法的,因为声明的变量传递给该方法是违法的.ISomeInterface

换句话说,如果变量声明为an ISomeInterface,则可以将其设置为null,纯粹且简单.这是因为接口是引用类型 - 因此,将对象声明为接口并将其分配给值类型对象框的值.

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

// 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;
}
Run Code Online (Sandbox Code Playgroud)

  • @Ben:我猜你说的是因为我的`ref`例子,我犹豫要包括因为我觉得它可能有点令人困惑.但我的观点是,如果将变量声明为`ISomeInterface`,则可以将其设置为null,这仅适用于引用类型.因此,将`ISomeInterface`变量设置为值类型的对象会导致装箱.这与值类型和引用类型有很大关系.如果将变量声明为特定值类型,则该变量*不能设置为null. (5认同)