I\'ve often wondered if the following scenario actually happens in c#
If I have a struct but I don\'t explicitly override any of the methods that derived from objec
If
thisType
is a value type andthisType
does not implement method then ptr is dereferenced, boxed, and passed as the 'this' pointer to the callvirt method instruction.This last case can occur only when method was defined on
Object
,ValueType
, orEnum
and not overridden bythisType
. In this case, the boxing causes a copy of the original object to be made.
The answer is yes, the value type is boxed. This is why it is always a good thing to override ToString()
on custom structs.
Edit: kek444's answer is correct. My apologies for misreading the question. I leave my answer here as I believe it has additional value and relevant information for future readers.
I also think that this quote from the reference in Mehrdad's answer is particularly thought provoking:
- If thisType is a value type and thisType does not implement method then ptr is dereferenced, boxed, and passed as the 'this' pointer to the callvirt method instruction.
This last case can occur only when method was defined on Object, ValueType, or Enum and not overridden by thisType. In this case, the boxing causes a copy of the original object to be made. However, because none of the methods of Object, ValueType, and Enum modify the state of the object, this fact cannot be detected.
One cannot, therefore, write a program to demonstrate that the boxing is taking place. It is only discernible by looking at the IL and completely understanding the constrained
prefix for the callvirt
instruction.
From section 11.3.5 of the C# language specification at http://download.microsoft.com/download/3/8/8/388e7205-bc10-4226-b2a8-75351c669b09/CSharp%20Language%20Specification.doc (http://msdn.microsoft.com/en-us/vcsharp/aa336809.aspx):
When a struct type overrides a virtual method inherited from System.Object (such as Equals, GetHashCode, or ToString), invocation of the virtual method through an instance of the struct type does not cause boxing to occur. This is true even when the struct is used as a type parameter and the invocation occurs through an instance of the type parameter type. For example:
using System;
struct Counter
{
int value;
public override string ToString() {
value++;
return value.ToString();
}
}
class Program
{
static void Test<T>() where T: new() {
T x = new T();
Console.WriteLine(x.ToString());
Console.WriteLine(x.ToString());
Console.WriteLine(x.ToString());
}
static void Main() {
Test<Counter>();
}
}
The output of the program is:
1
2
3
Although it is bad style for ToString to have side effects, the example demonstrates that no boxing occurred for the three invocations of x.ToString().
Similarly, boxing never implicitly occurs when accessing a member on a constrained type parameter. For example, suppose an interface ICounter contains a method Increment which can be used to modify a value. If ICounter is used as a constraint, the implementation of the Increment method is called with a reference to the variable that Increment was called on, never a boxed copy.
using System;
interface ICounter
{
void Increment();
}
struct Counter: ICounter
{
int value;
public override string ToString() {
return value.ToString();
}
void ICounter.Increment() {
value++;
}
}
class Program
{
static void Test<T>() where T: ICounter, new() {
T x = new T();
Console.WriteLine(x);
x.Increment(); // Modify x
Console.WriteLine(x);
((ICounter)x).Increment(); // Modify boxed copy of x
Console.WriteLine(x);
}
static void Main() {
Test<Counter>();
}
}
The first call to Increment modifies the value in the variable x. This is not equivalent to the second call to Increment, which modifies the value in a boxed copy of x. Thus, the output of the program is:
0
1
1
For further details on boxing and unboxing, see §4.3.
No it's not boxed when you call ToString
or GetHashCode
if it's implemented by your struct (why should it? constrained IL instruction takes care of it.) It's boxed when you call a non-virtual method (or a virtual method not overriden in the struct) on System.Object
(its base class), i.e. GetType
/MemberwiseClone
.
UPDATE: Sorry for the misunderstanding it may have caused. I wrote the answer with overriding the methods in the struct in mind (that's why I mentioned non-virtual methods need boxing, I should have been more explicit not to confuse readers, especially since I missed your statement regarding not overriding the method) as if you don't override it, the Object.ToString
method expects it's first argument (reference to this
) to be a reference type (an Object
instance). Obviously, the value has to be boxed in that call (as it's a call in the base class.)
However, the point is, the nature of calling a virtual method on a value type does not result in emitting the box
instruction (unlike non-virtual methods on Object
that always result in emitting an explicit box
instruction.) It's the callvirt
instruction that will do the boxing if it has to resort to the Object.ToString
implementation (as you mentioned in the updated question) just like when you are passing a struct to a method that expects an object
parameter.