(As a result of the study, in order to answer this question, I (I think I have!) Determined that the answer is βno.β However, I had to look in several different places to understand this, so I think that the question remains, but I wonβt be devastated if the community votes to close.)
For example:
void f<T>(T val) where T : IComparable { val.CompareTo(null); } void g() { f(4); }
Is 4 boxed? I know that explicitly casting a value type to an interface that implements box triggers:
((IComparable)4).CompareTo(null); // The Int32 "4" is boxed
What I donβt know is that passing a value type as a general parameter with an interface constraint is tantamount to casting - the language "where T is IComparable" suggests casting, but just turns T into IComparable it seems that it will defeat the whole purpose be generic!
To clarify, I would like to make sure that none of these events happen in the code above:
- When
g calls f(4) , 4 converted to IComparable , since there is an IComparable restriction on the parameter type f . - Assuming that (1) does not occur, inside
f , val.CompareTo(null) does not pass val from Int32 to IComparable to call CompareTo .
But I would like to understand the general case; not just what happens with int and IComparable s.
Now, if I put the code below in LinqPad:
void Main() { ((IComparable)4).CompareTo(null); f(4); } void f<T>(T val) where T : IComparable { val.CompareTo(null); }
And then examine the generated IL:
IL_0001: ldc.i4.4 IL_0002: box System.Int32 IL_0007: ldnull IL_0008: callvirt System.IComparable.CompareTo IL_000D: pop IL_000E: ldarg.0 IL_000F: ldc.i4.4 IL_0010: call UserQuery.f f: IL_0000: nop IL_0001: ldarga.s 01 IL_0003: ldnull IL_0004: constrained. 01 00 00 1B IL_000A: callvirt System.IComparable.CompareTo IL_000F: pop IL_0010: ret
It is clear that boxing occurs as expected for an explicit cast, but not a single box is obvious in f itself * or on its calling site in Main . This is good news. However, this is also one example with one type. This lack of boxing, what can be assumed for all cases?
* This MSDN article discusses the constrained prefix and indicates that its use in combination with callvirt will not start boxing for value types while the called method is implemented in the type itself (unlike the base class). I'm not sure if a type will always remain a value type when we are here.