Question

Do interface variables have value-type or reference-type semantics?

Interfaces are implemented by types, and those types are either value types or reference types. Obviously, both int and string implement IComparable, and int is a value type, and string is a reference type. But what about this:

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

(The question I was trying to answer was presumably deleted because it asked whether interfaces are stored on the stack or the heap, and, as we should all be aware, it's more constructive to think of differences between value and reference types in terms of their semantics rather than their implementation. For a discussion, see Eric Lippert's The stack is an implementation detail.)

Was it helpful?

Solution

Usually, as per the existing answers, it is a reference-type and requires boxing; there is an exception though (isn't there always?). In a generic method with a where constraint, it can be both:

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

This is a constrained operation, and is not boxed even if it is a value-type. This is achieved via constrained. Instead, the JIT performs the operation as virtual-call for reference-types, and static-call for value-types. No boxing.

OTHER TIPS

This is about understanding boxing and unboxing of types. In your example, the int is boxed upon assignment and a reference to that "box" or object is what is assigned to x. The value type int is defined as a struct which implements IComparable. However, once you use that interface reference to refer to the value type int, it will be boxed and placed on the heap. That's how it works in practice. The fact that using an interface reference causes boxing to occur by definition makes this reference type semantics.

MSDN: Boxing and Unboxing

A variable or field whose type is IComparable is a reference-type variable or field, regardless of the type of the value assigned to that field. This means that x in the sample code is boxed.

A simple test will demonstrate that. The test is based on the fact that 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();
    }

EDIT

On top of that, the C# specification specifically defines reference-type as comprising class types, interface types, array types and delegate types.

EDIT 2

As Marc Gravell points out in his answer, a generic type with an interface constraint is a different case. This doesn't cause boxing.

Variables of interface type will have always have either immutable semantics, mutable reference semantics, or "oddball" semantics (something other than normal reference or value semantics). If variable1 and variable2 are both declared as the same interface type, one performs variable2 = variable1, and one never again writes to either variable, the instance referred to by variable1 will always be indistinguishable from the one referred to be variable2 (since it will be the same instance).

Generic types with interface constraints may have immutable semantics, mutable reference semantics, or "quirky" semantics, but may also have mutable value semantics. This can be dangerous if the interface is not documented as having mutable value semantics. Unfortunately, there is no way to constrain an interface to have either immutable semantics or mutable value semantics (meaning that following variable2 = variable1, it should not be possible to change variable1 by writing variable2, nor vice versa). One could add a "struct" constraint along with the interface constraint, but that would exclude classes which have immutable semantics while not excluding structs that have reference semantics.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top