interface IBar { void Hidden(); }
class Foo : IBar { public void Visible() { /*...*/ } void IBar.Hidden() { /*...*/ } }
class Program
{
static T CallHidden1<T>(T foo) where T : Foo
{
foo.Visible();
((IBar)foo).Hidden(); //Cast required
return foo;
}
static T CallHidden2<T>(T foo) where T : Foo, IBar
{
foo.Visible();
foo.Hidden(); //OK
return foo;
}
}
Is there any difference (CallHidden1 vs. CallHidden2) is actual compiled code? Is there other differences between where T : Foo and where T : Foo, IBar (if Foo implements IBar) that in accessing explicitly implemented interface members ?
4/7 Generics Constraints. Previous: Next: Generics Interfaces. Constraints are like rules or instructions to define how to interact with a generic class or method. They can restrict the parameter that will be replaced with T to some certain type or class or have some properties, like to be new instance of class.
Interface Type Constraint You can constrain the generic type by interface, thereby allowing only classes that implement that interface or classes that inherit from classes that implement the interface as the type parameter.
C# allows you to use constraints to restrict client code to specify certain types while instantiating generic types. It will give a compile-time error if you try to instantiate a generic type using a type that is not allowed by the specified constraints.
Multiple interface constraints can be specified. The constraining interface can also be generic.
The IL generated is slightly different:
L_000d: ldarg.0
L_000e: box !!T
L_0013: callvirt instance void WindowsFormsApplication1.IBar::Hidden()
vs.
L_000d: ldarga.s foo
L_000f: constrained !!T
L_0015: callvirt instance void WindowsFormsApplication1.IBar::Hidden()
If T
were a value type, this would result in foo
being boxed in CallHidden1
but not in CallHidden2
. However, since Foo
is a class, any type T
derived from Foo
will not be a value type, and thus the behavior will be identical.
Yes, a tiny bit, since the second specifies that the interface must be implemented, which may become important if Foo
is later changed so that it does not implement IBar
.
That would make it unsuitable for being used in CallHidden2<>
while remaining valid at compile time for CallHidden1<>
(which would then fail at runtime if IBar
is no longer being implemented by Foo
).
So if they are in separate assemblies, the different metadata would make a difference. The executed IL will, however, be pretty similar if not the same.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With