see code snippet
public interface I0
{
void f0();
}
public struct S0:I0
{
void I0.f0()
{
}
}
public class A<E> where E :I0
{
public E e;
public void call()
{
e.f0();
}
}
here is IL code for call()
.maxstack 8
L_0000: ldarg.0
L_0001: ldflda !0 Temp.A`1<!E>::e
L_0006: constrained !E
L_000c: callvirt instance void Temp.I0::f0()
L_0011: ret
see reference for constrained
The constrained prefix can also be used for invocation of interface methods on value types, because the value type method implementing the interface method can be changed using a MethodImpl. If the constrained prefix is not used, the compiler is forced to choose which of the value type’s methods to bind to at compile time. Using the constrained prefix allows the MSIL to bind to the method that implements the interface method at run time, rather than at compile time.
That means it will call one method containing interface method code of f0 without boxing the struct.
Do any other ways caling interface method without boxing exist as above GenericClass in C#?
It depends… you specifically say you don’t want a generic class… the only other option is a generic method in a non-generic class. The only other time you can get the compiler to emit a
constrainedcall is if you callToString(),GetHashCode()orEquals()(fromobject) on astruct, since those are thenconstrained– if thestructhas anoverridethey will becall; if it doesn’t have anoverride, they will becallvirt. Which is why you should alwaysoverridethose 3 for anystruct;p But I digress. A simple example would be a utility class with some static methods – extension methods would be an ideal example, since you also get the advantage that the compiler will switch automatically between the public/implicit API and the extension/explicit API, without you ever needing to change code. For example, the following (which shows both an implicit and explicit implementation) has no boxing, with onecalland oneconstrained+callvirt, which will implemented viacallat the JIT:And here’s the key bits of IL:
One downside of an extension method, though, is that it is doing an extra copy of the
struct(see theldloc.0) on the stack, which might be a problem if it is either oversized, or if it is a mutating method (which you should avoid anyway). If that is the case, arefparameter is helpful, but note that an extension method cannot have aref thisparameter – so you can’t do that with an extension method. But consider:with:
which is:
with:
Still no boxing, but now we also never copy the struct, even for the explicit case.