简体   繁体   中英

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.

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

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.

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.

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.

Often the interface constraint is combined with eg 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM