I could define an IHaveXYZ interface, but it seems to me that that will result in a lot of boxing operations which defies the spirit of the whole plan (and is less effecient, if that matters).
No, it won't - if you use a generic type constraint, the generated IL will not box/unbox. For example:
interface IFoo
{
void Foo();
}
struct Bar : IFoo
{
public void Foo()
{
// Do something
}
}
class Test
{
static void DoFoo<T>(T value) where T : IFoo
{
value.Foo();
}
static void Main()
{
Bar bar = new Bar();
DoFoo(bar); // No boxing involved
}
}
The IL for DoFoo
looks like this:
.method private hidebysig static void DoFoo<(IFoo) T>(!!T 'value') cil managed
{
// Code size 16 (0x10)
.maxstack 8
IL_0000: nop
IL_0001: ldarga.s 'value'
IL_0003: constrained. !!T
IL_0009: callvirt instance void IFoo::Foo()
IL_000e: nop
IL_000f: ret
} // end of method Test::DoFoo
Note the "constrained" part. From ECMA-335 section I.8.2.4:
Boxing and unboxing of generic arguments adds performance overhead to a CLI implementation. The
constrained.
prefix can improve performance during virtual dispatch to a method defined by a value type, by avoiding boxing the value type.
It's important that you don't just refer to value
as IFoo
though. This line would box the value (when T
is a value type):
IFoo copy = value; // Might box
So long as you stick to the generic type parameter, you should be okay.