Structs, Interfaces and Boxing [duplicate]
Asked Answered
S

4

58

Possible Duplicate:
Is it safe for structs to implement interfaces?

Take this code:

interface ISomeInterface
{
    public int SomeProperty { get; }
}

struct SomeStruct : ISomeInterface
{
    int someValue;

    public int SomeProperty { get { return someValue; } }

    public SomeStruct(int value)
    {
        someValue = value;
    }
}

and then I do this somewhere:

ISomeInterface someVariable = new SomeStruct(2);

is the SomeStruct boxed in this case?

Scoria answered 13/6, 2010 at 15:26 Comment(0)
E
75

Yes, it is. Basically whenever you need a reference and you've only got a value type value, the value is boxed.

Here, ISomeInterface is an interface, which is a reference type. Therefore the value of someVariable is always a reference, so the newly created struct value has to be boxed.

Embracery answered 13/6, 2010 at 15:29 Comment(6)
I assumed that. Not entirely sure what made me doubt that would be the case. Just thought I'd throw it out here just in case someone else got the odd wondering.Scoria
Give a man a tool to get answers (Red Gate Reflector), and he'll have answers for life. But give him just one answer and he'll be back again with more questions and more SO rep points...Navarro
@Ben: On the other hand, give a man a tool and they'll have to check it every time they're unsure. Give a man an explanation and they'll be able to reason about it for themselves.Embracery
Note also the slight edge-case covered in my separate reply.Slush
@ben dilasm actually did answer the question for me just before Jon answered. Though just throwing the question out there has given extra answers. Marc answer shows how sometime it can seem your using an interface thats a struct that doesn't get boxed, and it was probably sometime ago I saw that behaviour which might have been what triggered my question.Scoria
Give a man a disassembler and he'll get implementation details that are subject to change, not necessarily contractual behavior. And then you have fun Old New adventures devblogs.microsoft.com/oldnewthing/20031223-00/?p=41373Convexoconcave
S
104

Jon's point is true, but as a side note there is one slight exception to the rule; generics. If you have where T : ISomeInterface, then this is constrained, and uses a special opcode. This means the interface can be used without boxing. For example:

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

This does not involve boxing, even for value-type T. However, if (in the same Foo) you do:

ISomeInterface asInterface = obj;
asInterface.Bar();

then that boxes as before. The constrained only applies directly to T.

Slush answered 13/6, 2010 at 18:13 Comment(6)
hai, it won't be boxed because the method called after all the generics are resolved is void Foo(SomeStruct obj) not void Foo(ISomeInterface obj)Scoria
@Sekhat: generic parameters are resolved at runtime so the compiler doesn't know the method is called with with a value type.Holds
@Scoria - to expand on @adrianm's point: the same IL is used for all callers. Each value-type param gets JITted separately, but all ref-types share a JIT. The compiler has nothing to do with this; .NET generics are runtime, not compile-time. The signature is Foo(T obj) in every case.Slush
I never stated whether the generics was resolved at compile time or runtime. However they are resolved at some point. Foo<T> (T obj) is a better fit for SomeStruct than Foo(ISomeInterface interface) because the generic is eventually resolved to mean Foo(SomeStruct obj).Scoria
@MarcGravell call explicitly implemented interface method of struct without boxing another question asking about this situationKarnak
+1 thats a very good point, easy to missCrosspiece
E
75

Yes, it is. Basically whenever you need a reference and you've only got a value type value, the value is boxed.

Here, ISomeInterface is an interface, which is a reference type. Therefore the value of someVariable is always a reference, so the newly created struct value has to be boxed.

Embracery answered 13/6, 2010 at 15:29 Comment(6)
I assumed that. Not entirely sure what made me doubt that would be the case. Just thought I'd throw it out here just in case someone else got the odd wondering.Scoria
Give a man a tool to get answers (Red Gate Reflector), and he'll have answers for life. But give him just one answer and he'll be back again with more questions and more SO rep points...Navarro
@Ben: On the other hand, give a man a tool and they'll have to check it every time they're unsure. Give a man an explanation and they'll be able to reason about it for themselves.Embracery
Note also the slight edge-case covered in my separate reply.Slush
@ben dilasm actually did answer the question for me just before Jon answered. Though just throwing the question out there has given extra answers. Marc answer shows how sometime it can seem your using an interface thats a struct that doesn't get boxed, and it was probably sometime ago I saw that behaviour which might have been what triggered my question.Scoria
Give a man a disassembler and he'll get implementation details that are subject to change, not necessarily contractual behavior. And then you have fun Old New adventures devblogs.microsoft.com/oldnewthing/20031223-00/?p=41373Convexoconcave
W
12

I'm adding this to hopefully shed a little more light on the answers offered by Jon and Marc.

Consider this non-generic method:

public static void SetToNull(ref ISomeInterface obj) {
    obj = null;
}

Hmm... setting a ref parameter to null. That's only possibly for a reference type, correct? (Well, or for a Nullable<T>; but let's ignore that case to keep things simple.) So the fact that this method compiles tells us that a variable declared to be of some interface type must be treated as a reference type.

The key phrase here is "declared as": consider this attempt to call the above method:

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);

Granted, the reason you can't pass x in the above code to SetToNull is that x would need to be declared as an ISomeInterface for you to be able to pass ref x -- and not because the compiler magically knows that SetToNull includes the line obj = null. But in a way that just reinforces my point: the obj = null line is legal precisely because it would be illegal to pass a variable not declared as an ISomeInterface to the method.

In other words, if a variable is declared as an ISomeInterface, it can be set to null, pure and simple. And that's because interfaces are reference types -- hence, declaring an object as an interface and assigning it to a value type object boxes that value.

Now, on the other hand, consider this hypothetical generic method:

// 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;
}
Woodrowwoodruff answered 13/6, 2010 at 18:48 Comment(2)
This has nothing to do with value types and reference types, and everything to do with variance.Navarro
@Ben: I'm guessing you're saying that because of my ref example, which I hesitated to include because I thought it might be a bit confusing. But my point is that if a variable is declared as an ISomeInterface, it can be set to null, which is only true of a reference type. Therefore setting an ISomeInterface variable to a object of value type incurs boxing. This does have very much to do with value types and reference types. If a variable is declared as a particular value type, that variable cannot be set to null.Woodrowwoodruff
P
1

The MSDN documentation tells us that structs are value, not reference types. They are boxed when converting to/from a variable of type object. But the central question here is: what about a variable of an interface type? Since the interface can also be implemented by a class, then this must be tantamount to converting from a value to a reference type, as Jon Skeet already said, therefore yes boxing would occur. More discussion on an msdn blog.

Puttee answered 13/6, 2010 at 16:56 Comment(1)
The simplest way to think about this issue is to recognize that every variable, parameter, or field needs to have some concrete allocation type in addition to a (possibly empty) combination of interfaces. If no other concrete type is available, the system will assume an object reference.Devitrify

© 2022 - 2024 — McMap. All rights reserved.