(As a result of doing the research to answer this question, I (think I have!) determined that the answer is "no." However, I had to look in several different places to figure this out, so I think there is still value to the question. 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 it implements triggers boxing:
((IComparable)4).CompareTo(null); // The Int32 "4" is boxed
What I don't know is whether passing a value type as a generic parameter with an interface constraint is tantamount to performing a cast--the language "where T is an IComparable" sort of suggests casting, but simply turning T
into IComparable
seems like it would defeat the entire purpose of being generic!
To clarify, I would like to be sure neither of these things happens in the code above:
- When
g
callsf(4)
, the4
is cast toIComparable
since there is anIComparable
constraint onf
's parameter type. - Assuming (1) does not occur, within
f
,val.CompareTo(null)
does not castval
fromInt32
toIComparable
in order to callCompareTo
.
But I would like to understand the general case; not just what happens with int
s and IComparable
s.
Now, if I put the below code into 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's clear that boxing occurs as expected for the explicit cast, but no boxing is obvious either in f
itself* or at its call site in Main
. This is good news. However, that's also just one example with one type. Is this lack of boxing something that can be assumed for all cases?
*This MSDN article discusses the constrained
prefix and states that using it in conjunction with callvirt
will not trigger boxing for value types as long as the called method is implemented on the type itself (as opposed to a base class). What I'm not sure of is whether the type will always still be a value type when we get here.
constrained
does not occur outside the method, passing the value type to the method will not be boxed. However, theconstrained callvirt
inside the method might box the value type depending on circumstances. What exactly is your question here? – Emmeramenum
is passed as anIComparable
-constrained generic, will callingCompare
on it cause the constrained generic to be boxed"? I would guess that in the case of theIComparable
interface implemented bySystem.Enum
the answer would be that such a call will not only involve boxing, but other horribly slow Refection-based nastiness as well, but types with "user-defined" interface implementations boxing will be avoided. – Jimint
to a generic methodf
withwhere T : IComparable
might actually cast theint
to anIComparable
at the call site and pass it that way. And if it didn't, I was also worried that perhaps theT
would first be cast toIComparable
withinf
in order to callCompareTo
on it. My experiment showed that neither happens in the specific case I examined. – Slideaction