Summary of answers
My confusion about generic interfaces and boxing/unboxing came from the fact I knew C# generics enabled us to produce more efficient code.
For example, the fact int
implements IComparable<T>
and IComparable
meant to me:
IComparable
was to be used with old, pre-generics code, but would mean boxing/unboxing
IComparable<T>
was to be used to generics enabled code, supposedly avoiding boxing/unboxing
Eric Lippert's comment is as simple, clear and direct as it can be:
Generic interface types are interface types. There's nothing special about them that magically prevents boxing
From now, I know without doubt that casting a struct into an interface will imply boxing.
But then, how IComparable<T>
was supposed to work more efficiently than IComparable
?
This is where supercat's answer (edited by Lasse V. Karlsen) pointed me to the fact generics were more like C++ templates than I thought:
The purpose of IComparable is to allow for something like:
void bar<T>(T value) where T : IComparable<T> { /* etc. */ }
Which is quite different from:
void bar(IComparable<T> value) { /* etc. */ }
Or even:
void bar(IComparable value) { /* etc. */ }
My guess is that for the first prototype, the runtime will generate one function per type, and thus, avoid boxing issues when dealing with structs.
Whereas, for the second prototype, the runtime will only generate functions with an interface as a parameter, and as such, do boxing when T is a struct. The third function will just box the struct, no more, no less.
(I guess this is where C# generics combined with C# structs show their superiority when compared with Java type-erasure generics implementation.)
Merlyn Morgan-Graham's answer provided me with an example of test I'll play with at home. I'll complete this summary as soon as I have meaningful results (I guess I'll try to use pass-by-reference semantics to see how all that works...)