接口变量是否具有值类型或引用类型语义?

时间:2011-12-16 22:34:06

标签: c# .net interface value-type reference-type

接口变量是否具有值类型或引用类型语义?

接口按类型实现,这些类型是值类型或引用类型。显然,intstring都实现IComparableint是值类型,string是引用类型。但是这个怎么样:

IComparable x = 42;
IComparable y = "Hello, World!";

(我试图回答的问题可能被删除了,因为它询问接口是存储在堆栈还是堆上,而且,正如我们都应该知道的那样,考虑值和引用类型之间的差异更具建设性。它们的语义而不是它们的实现。有关讨论,请参阅Eric Lippert的The stack is an implementation detail。)

4 个答案:

答案 0 :(得分:13)

通常,根据现有的答案,它是一个参考类型,需要拳击;但有一个例外(总是不存在吗?)。在具有where约束的通用方法中,它可以是两者

void Foo<T>(T obj) where T : ISomeInterface {
    obj.SomeMethod();
}

这是约束操作,即使是值类型,也不会装箱。这是通过constrained实现的。相反,JIT执行操作作为引用类型的虚拟调用,以及对值类型的静态调用。没有拳击。

答案 1 :(得分:5)

这是关于理解拳击和拆箱类型。在您的示例中,int在赋值时被加框,并且对该“box”或对象的引用是分配给x的内容。值类型int被定义为实现IComparable的结构。但是,一旦使用该接口引用引用值类型int,它将被装箱并放在堆上。这就是它在实践中的运作方式。使用接口引用导致装箱按定义发生的事实使得此引用类型语义。

MSDN: Boxing and Unboxing

答案 2 :(得分:3)

类型为IComparable的变量或字段是引用类型变量或字段,与分配给该字段的值的类型无关。这意味着示例代码中的x已装箱。

一个简单的测试将证明这一点。该测试基于you can only unbox a value type to its original type (and the nullable version of that type)

这一事实
    [TestMethod, ExpectedException(typeof(InvalidCastException))]
    public void BoxingTest()
    {
        IComparable i = 42;
        byte b = (byte)i;      //exception: not allowed to unbox an int to any type other than int
        Assert.AreEqual(42, b);
        Assert.Fail();
    }

修改

最重要的是,C#规范明确将 reference-type 定义为包含类类型,接口类型,数组类型和委托类型。

编辑2

正如Marc Gravell在他的回答中指出的那样,具有接口约束的泛型类型是不同的情况。这个不会导致拳击。

答案 3 :(得分:2)

接口类型的变量将始终具有不可变语义,可变引用语义或“古怪”语义(除了普通引用或值语义之外的其他语义)。如果variable1variable2都声明为相同的接口类型,则执行variable2 = variable1,并且一个永远不会写入任何一个变量,variable1引用的实例将始终与被称为variable2的人无法区分(因为它将是同一个实例)。

具有接口约束的泛型类型可能具有不可变语义,可变引用语义或“古怪”语义,但也可能具有可变值语义。如果未将接口记录为具有可变值语义,则这可能是危险的。不幸的是,没有办法限制接口具有不可变的语义或可变的值语义(意味着跟随variable2 = variable1,不应该通过编写variable1来改变variable2,也不可能反之亦然)。可以添加“struct”约束以及接口约束,但这将排除具有不可变语义的类,而不排除具有引用语义的结构。