If we have a struct and a class they will be passed differently if you call a function with them as parameter.
struct MyStruct{}
class MyClass{}
// s is a value (struct) which passed by value
private void WriteStruct(MyStruct s)
{
Console.WriteLine(s);
}
// c is a reference to an class-instance which is passed by value
private void WriteClass(MyClass c)
{
Console.WriteLine(c);
}
This behaviour is described here, here and here.
Now what happens if we have a function which accepts object
.
private void Write(object obj)
{
Console.WriteLine(obj);
}
I'm aware of boxing and assume that it'll just be boxed implicitly but I'm unsure in this case, mostly because ValueType
inherits from Object
(source) and also because I don't see why it wouldn't just be passed as is without any boxing since that obviously works (at least) when the signature also uses that struct.
Will calling this method with a struct (or any other value type) box the struct and actually pass a reference or will the struct itself be passed?
Ps. I don't know how you could test this because if you were to cast it to the struct in the function, a new struct would be created1 and the reference (if there was one) would be lost anyway. Thats why the following code doesn't change the value no matter how obj
was passed.
private static void ChangeObj(object obj)
{
var s = (MyStruct)obj; // this will create a new struct
s.MyInt = 400;
} // s is now lost, the passed in value is unchanged
EDIT:
TheGeneral mentioned in the comments to head over to sharplab. I have no clue about IL but when looking at the following code, I can see some interesting differences.
MyStruct s = new MyStruct();
MyClass c = new MyClass();
WriteStruct(s);
WriteClass(c);
Write(s);
- The
WriteStruct(s)
instruction contains an IL instructioncall instance void Test::WriteStruct(valuetype MyStruct)
(and some other stuff I don't get). - The
WriteClass(c)
instruction contains an IL instructioncall instance void Test::WriteClass(class MyClass)
(and some other stuff I don't get). - The
Write(s)
instruction (drumroll) contains an IL instructionbox MyStruct
(woo) followed bycall instance void Test::Write(object)
I guess this means the value is indeed boxed.. Is there anything to add to this?
I'm guessing it has to be boxed since the runtime treats all object
s which are explicitly marked as object
as references, is that correct?
If so, why is there this class
keyword in the signature of the IL-call instance
instruction when calling WriteClass
but not when calling Write
? There's just object
without any class
or valuetype
keyword. Is this too much of implementation details and IL-gibbrish (btw IL looks cool, might look into that more) to include in an answer here or is it possible to explain this in an easy way?
1 Talking about creating might be wrong here since the values are just copied. MSDN says in the boxing-unboxing-article: Copying the value from the instance into the value-type variable.