user610650
user610650

Reputation:

Interface constraint on generic method arguments

In my quest to understand C# properly, I find myself asking what are the practical differences between specifying an interface constraint on a generic method argument, and simply specifying the interface as the type of the argument?

public interface IFoo
{
    void Bar();
}

public static class Class1
{
    public static void Test1<T> (T arg1) where T : IFoo
    {
        arg1.Bar();
    }

    public static void Test2(IFoo arg1)
    {
        arg1.Bar();
    }
}

EDIT

I know my example is very narrow as it's just an example. I'm quite interested in differences that go outside its scope.

Upvotes: 17

Views: 1316

Answers (5)

Avner Shahar-Kashtan
Avner Shahar-Kashtan

Reputation: 14700

I'd just like to put some emphasis on the answers others have given.

There IS a difference between Test(IFoo foo) and Test<T>(T foo) where T : IFoo. There's a real difference just like there's a huge difference between List<object> (or, let's say ArrayList, which receives an object) and List<string>.

Test (IFoo foo), gives you the benefits of polymorphism and type inheritance, just like List<object>. It allows you to built one class which handles all IFoo types. But sometimes I don't just want the polymorphism, I want a list that can only hold strings - and List<string> gives me that without requiring me to write a strongly-typed wrapper over ArrayList.

Same for your code. Let's say I have a class Comparer<T> where T:IFoo. I want to be able to use this class to compare Foo1 objects to each other, or to compare Foo2 to each other, but I don't want to be able to compare Foo1 to Foo2. A strongly typed generic method will enforce that, while a polymorphic one wouldn't:

public class Comparer
{
    public bool Compare1<T>(T first, T second) where T : IFoo {...}
    public bool Compare2 (IFoo first, IFoo second) {...}
}

Foo1 first = new Foo1();
Foo2 second = new Foo2();
myComparer.Compare1(first, second); // won't compile!
myComparer.Compare2(first, second); // Compiles and runs.

Upvotes: 4

NSGaga
NSGaga

Reputation: 14302

Often the interface constraint is combined with e.g. IFoo, new()

...which allows you to manipulate objects as T entirely, create, initialize collections etc. - and return T as suggested. While with just interface you don't know which class (T) it is really.

Upvotes: 0

Ucodia
Ucodia

Reputation: 7700

It is all a matter of type casting. If your method must return T, then Test1 will require no casting and as Test2 returns only an interface, you will need explicit or implicit type casting to get the final type.

Upvotes: 0

Teoman Soygul
Teoman Soygul

Reputation: 25732

There isn't a scrap of difference for the example you've given. On the other hand, using the generic version gives you the ability to extend the constraint list (where T : IFoo, IOther) in the future, without changing the method signature.

Upvotes: 6

Daniel Hilgarth
Daniel Hilgarth

Reputation: 174299

In your specific example there is no difference. But take the following method:

public static class Class1
{
    public static T Test1<T>(T arg1) where T : IFoo
    {
        arg1.Bar();
        return arg1;
    }

    public static IFoo Test2(IFoo arg1)
    {
        arg1.Bar();
        return arg1;
    }
}

Test1 will return the specific type of arg1, whereas Test2 will only return the interface. This is often used in fluent interfaces.


Extended example:

public interface IFoo
{
    void Bar();
}

public class Foo : IFoo
{
    // implementation of interface method
    public void Bar()
    {
    }

    // not contained in interface
    public void FooBar()
    {
    }
}


var foo = new Foo();
Class1.Test1(foo).FooBar(); // <- valid
Class1.Test2(foo).FooBar(); // <- invalid

Upvotes: 12

Related Questions