Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explicitly implemented interface and generic constraint

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 ?

like image 796
Tomáš Holan Avatar asked Feb 05 '10 16:02

Tomáš Holan


People also ask

What is a generic constraint?

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.

What does the generic constraint of type interface do?

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.

What is generic type constraints in C#?

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.

Can a generic class have multiple constraints?

Multiple interface constraints can be specified. The constraining interface can also be generic.


2 Answers

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.

like image 101
kvb Avatar answered Oct 12 '22 23:10

kvb


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.

like image 41
Lucero Avatar answered Oct 12 '22 23:10

Lucero