12

What is the difference between this:

void MyMethod(IMyInterface value)
{
    //...
}

and this:

void MyMethod<T>(T value) where T : IMyInterface
{
    //...
}
Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794

7 Answers7

11

The main functional difference is that you can know the actual type of the object inside of the generic method. The T parameter will contain the actual type which can advantageous in certain scenarios.

In the non-generic case you cannot guarantee access to the underlying type of the object. Most of the type you could grab value.GetType() but the user could pass Null and thwart you.

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • This is incorrect, he is constraining T to be an IMyInterface. – joshperry Feb 27 '09 at 15:53
  • @joshperry, yes but T will still point to the actual type of the value passed in. More accurately it will point to the type of the reference at the callsite of the method. I am 100% percent confident that I am correct on this point. – JaredPar Feb 27 '09 at 15:54
  • Well, 98 % ;-p If the caller has used *implict* generic type inference, then T will be the type of the *variable* (not necessarily the reference, since the reference could be a subclass). If the caller has used *explicit* notation, then again, then they could have different variable, T and instance. – Marc Gravell Feb 27 '09 at 15:58
  • @joshperry, no apology needed. Last night I spent a good 10 minutes typing up a full page answer to a question with several examples. Joel then pointed out I misread the question and provided an answer to a different problem. I ended up keeping the answer as a WIKI – JaredPar Feb 27 '09 at 15:59
  • @Marc, doh, forgot about that one. – JaredPar Feb 27 '09 at 15:59
  • i.e. class Foo : IMyInterface {} class Bar : Foo {}; Foo foo = new Bar(); MyMethod(foo); [variable is Foo, T is Foo, obj is Bar]; MyMethod(foo); [variable is Foo, T is IMyInterface, obj is Bar] – Marc Gravell Feb 27 '09 at 16:00
  • Just because I was curious, and so others might be as well, Jared was referring to this answers (apparently, leppie is also Joel): http://stackoverflow.com/questions/592584/what-is-lambda-lifting/593491#593491 – Joel Coehoorn Feb 27 '09 at 17:12
  • @Joel, doh, like the answer said, I needed sleep :( – JaredPar Feb 27 '09 at 17:32
7

Jared has mentioned some of the points; another interesting one: with generics, you can avoid boxing of value-types as long as you basically don't touch it... so I could have a struct Foo : IMyInterface and pass it in, and it won't get boxed.

The difference gets more noticeable with things like collections:

static void Foo(IEnumerable<IMyInterface> data) {}

vs

static void Foo<T>(IEnumerable<T> data) 
    where T : IMyInterface {}

Now, since C# 3.0 doesn't have covariance (except for arrays), I can't pass a List<Bar> to the top one, even if Bar : IMyInterface - but I can with the second (implicit T = Bar).

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Things are more interesting with `IEnumerator`, since some of the more common enumerators are actually structures. – supercat Sep 02 '13 at 19:13
  • @supercat yes, but if you use `IEnumerable.GetEnumerator()`, you can't use that; it would only use that version for `foreach` over the thing itself – Marc Gravell Sep 02 '13 at 21:05
  • If you use `IEnumerator.GetEnumerator()` that's correct. If, however, you say `using(var myEn = myList.GetEnumerator(); doSomething(ref myEn);` and `doSomething` takes an `IEnumerator`-constrained generic, it can receive the thing as a structure (note that if one isn't careful to always pass it by `ref` the semantics will differ from the bare interface type; additionally, assignment of the generic type may take a snapshot of the enumeration state (works with some struct-type enumerators, and can't work with any class-type ones). – supercat Sep 02 '13 at 21:23
3

The generic version will require .NET 2.0.

But seriously, while they look similar, there are fundamental differences between them. One difference is, at runtime, the JIT compiler will generate code for each value type that will be used for the generic version. The non-generic version will require the value types to be boxed in order to be passed to the function.

The difference will also matter when dealing with delegates. The signature of MyMethod<int> matches void MyDelegate(int x) while the non-generic version is not matched.

Mehrdad Afshari
  • 414,610
  • 91
  • 852
  • 789
3

Another difference, using the generic method allows you to specify multiple interfaces that your object must implement:

class Dictionary<TKey,TVal>
    where TKey: IComparable, IEnumerable
    where TVal: IValue
{ ... }
Frank Krueger
  • 69,552
  • 46
  • 163
  • 208
1

Another subtle difference is that you can't overload a method only on constraints (constraints aren't part of the signature of the method):

This is illegal:

void MyMethod<T>(T value) where T : IMyInterface
{
    //...
}

void MyMethod<T>(T value) where T : IMyInterface2
{
    //...
}

while this is legal:

void MyMethod(IMyInterface value)
{
    //...
}

void MyMethod(IMyInterface2 value)
{
    //...
}
xanatos
  • 109,618
  • 12
  • 197
  • 280
0

Yet another difference for generic methods in general (though not for your example) is that if one has a method like T MungeThing<T>(T it) where T:IMungeable<T> and class Fnord implements IMungeable<Fnord>, then code will be able to say: Fnord thing1, thing2; ... thing1 = MungeThing(thing2); and the compiler will know that MungeThing will return a Fnord rather than some arbitrary implementation of IMungable.

supercat
  • 77,689
  • 9
  • 166
  • 211
0

Another caveat to consider with this scenario is the fact that using "where T : <%your base interface or abstraction%>" can be overused in generics rendering your generic type non-generic in nature.

IE: Remember that by isolating your generic method to IMyInterface, you're isolating that method to only those types implementing IMyInterface. So if you've merely chosen to use IMyInterface based on good OOP principles, but you have only one (or in some cases a very small number of) potential type anywhere that'll be implementing that interface, then you've defeated the purpose of using generics. Under that circumstance, the first option would be better.

Only use "where" on your generic type when you're going to have a broader range of types that actually implement IMyInterface.

TheHolyTerrah
  • 2,859
  • 3
  • 43
  • 50