3

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 instruction call instance void Test::WriteStruct(valuetype MyStruct) (and some other stuff I don't get).
  • The WriteClass(c) instruction contains an IL instruction call instance void Test::WriteClass(class MyClass) (and some other stuff I don't get).
  • The Write(s) instruction (drumroll) contains an IL instruction box MyStruct (woo) followed by call 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 objects 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.

Joelius
  • 3,839
  • 1
  • 16
  • 36
  • https://sharplab.io/ check if it boxes for your self – TheGeneral Sep 01 '19 at 20:58
  • Sadly I have absolutely no clue about IL.. But I'll look into it thanks :) – Joelius Sep 01 '19 at 21:06
  • Thank you @TheGeneral, I got a good start :) Please see my edit – Joelius Sep 01 '19 at 21:18
  • Most probably, it will create a boxed local copy of your struct and then pass-by-value a reference to the locally created object. Can I ask you what are you trying to achieve? The general rule is that structs are always treated as values. – Lucio M. Tato Sep 01 '19 at 21:29
  • This may be the one instance of a "type keyword" in the IL language. The alternative would've been `class System.Object`. Keep in mind that IL is just a textual representation of the underlying binary code. As a comparison, in 16-bit x86 assembly, the instruction `mov ax, 0` is `B8 00 00`, but if I wanted to write another assembler where `cp 0 -> #ax` results in the same three bytes, that would be just as good. For *learning* purposes, try not to put too much value in *how* it's represented and put more into *what* is being represented. – madreflection Sep 01 '19 at 21:35
  • That said, for *production* purposes, representation is very important because it can make maintenance easier or harder depending on how clearly you write something. – madreflection Sep 01 '19 at 21:39
  • It probably should be noted that, structs really should be immutable – TheGeneral Sep 01 '19 at 22:35
  • @LucioM.Tato Trying to achieve might be the wrong word. I'm trying to write a pro-cons list for using `struct` in a certain scenario. Initially I used `struct` because it has value semantics and doesn't need any inheritance. However I found that there are almost no benefits in my case. One thing I wasn't sure about is if there is a theoretical performance hit because it will be passed into a function as an object. I now know this does in fact box the value and give me a small performance penalty. In my case this really wouldn't matter but I didn't want to say anything wrong. – Joelius Sep 02 '19 at 14:07
  • @madreflection I know what IL is but again, don't know anything about how to read it (I have not gone that low-level yet). I don't quite understand what you mean by _production purposes_. You said the _representation_ is important there but what _production_ and what _representation_ (my code, the IL, the actual bytes) do you mean? – Joelius Sep 02 '19 at 14:10
  • @TheGeneral Yes I know that this is one of the most important guidelines. This is also one of the reasons I think not using `struct` and just going with `class` is better in my case (it can contain a mutable reference type and that's not very desirable for an immutable struct, read [this answer](https://stackoverflow.com/a/945708/10883465)). – Joelius Sep 02 '19 at 14:12
  • By "production", I simply meant *producing* or writing code in an actual product, for which the previous comment's advice would have been wrong. My point is that how IL represents `object` is inconsequential. It's `object`. You're asking about a special case in representation but your question reads as if you're trying to find some meaning in it and there's not. – madreflection Sep 02 '19 at 14:32
  • You asked *"why is there this `class` keyword in the signature of the IL-call instance instruction when calling `WriteClass` but not when calling `Write`?"* I'm saying that `object` in the signature of `Write` might as well be `class System.Object` and don't read too much into that. I could ask the same thing about C, which requires `struct` before the type name *everywhere* you use it. That's just how the language was designed. C++ relaxed that requirement. C could have relaxed it but didn't. – madreflection Sep 02 '19 at 14:41

0 Answers0