14

Suppose I have three methods:

void Foo(MemoryStream v) {Console.WriteLine ("MemoryStream");}
void Foo(Stream v)       {Console.WriteLine ("Stream");}
void Foo(object v)       {Console.WriteLine ("object");}

I call method Foo passing first parameter of open generic type:

void Bar<T>()
{
    Foo(default(T)); //just to show the scenario
    //default(T) or new T() doesn't make a difference, null is irrelevant here
}

I want to call MemoryStream overload, so I close generic type of method Bar with MemoryStream:

Bar<MemoryStream>();

but the object overload is called. If I add generic constraint to Foo signature where T : Stream, then the Stream version is called.

Is there a way to dispatch method call to MemoryStream overload, based on open generic type T?

I don't want to use Delegate.CreateDelegate or other Reflection APIs. Just in the means of C# language. I'm probably missing something within the language itself.

Tried this scenario with value types as closed generic type and using static methods.

Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
Ilya Ivanov
  • 23,148
  • 4
  • 64
  • 90
  • `default(T)` for `MemoryStream` is `null`, so the object overload is called. looks ok to me. – Stephane Delcroix Oct 21 '13 at 10:28
  • @StephaneDelcroix if I add `where T : stream` then `default(T)` is still null, but other method is called. Check msdn for overload resolution. Compiler can guess the **type** of an expression, even if the resulting value is null. Example `Foo((MemoryStream)null)` will call the correct overload with `null` as parameter. – Ilya Ivanov Oct 21 '13 at 10:29
  • 5
    It should be noted that "doing different things based on different `T`" is pretty much the exact opposite of the intent of "generics", which is essentially *defined* as "applying the same pattern for any different `T`" - it should not be surprising that generics *by itself* doesn't help you here... – Marc Gravell Oct 21 '13 at 10:33
  • 1
    What you are suggesting is essentially a virtual call. I'm afraid the only way to do that is to either implement your own vtable pattern or use dynamic dispatch in c#. – ghord Oct 21 '13 at 10:34
  • 1
    BTW replacing `default(T)` with `new T()` STILL calls the `object` overload. This isn't about nulls but about generics and overloads in general! – Panagiotis Kanavos Oct 21 '13 at 10:35
  • Actually, this is a case of *template specialization* which doesn't work in C#. There is even an answer [here](http://stackoverflow.com/questions/2229084/c-sharp-generic-interface-specialization), which kind of makes this a duplicate – Panagiotis Kanavos Oct 21 '13 at 10:44

6 Answers6

10

What you describe is called "template specialization" and doesn't work in C#. It is available in C++ but still hasn't made its way to C#.

This has already been answered in "C# generic interface specialization". The short version is that you can't do it. You can work around it forcing runtime resolution but in this case using generics makes no sense. Generics should be used to use the same code on different types.

Perhaps there is another way of doing what you really want. I've run in similar situations when implementing the Strategy or Template Method patterns, where I want most of the code to work in the general case but modify some specific steps.

In such cases it's better to inject the custom steps to your class as interfaces, or even Func<> objects that specialize the behavior, when you actually create the "Template Method".

Of course, there are a lot of other ways to do this, some of which work better than others for specific problems

Community
  • 1
  • 1
Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • 2
    I love your answer and Marc's comment. They provide nice and deep guidance on generics intend and usage. I've never though about them purely in a sense, that the same functionality is applied over same data, even thou I've read this over and over again in msdn and books on C#. I've always thought about them as parameterising a type or method with open type, which is a huge oversimplification of generics primal intent – Ilya Ivanov Oct 21 '13 at 23:03
9

This can only be done using dynamic binding, e.g. like this:

void Bar<T>(T value)
{
    dynamic parameter = value;
    Foo(parameter); 
}

Note that dynamic dispatch uses the actual runtime type of the actual runtime object to do method dispatch, so there has to be an object. If value is null, this will not work.

Kris Vandermotten
  • 10,111
  • 38
  • 49
  • this wont help, just remove MemoryStream overloaded method and see it for yourself. :) – Alagesan Palani Oct 21 '13 at 11:04
  • or just call Bar(), it will still call MemoryStream overloaded in your case. – Alagesan Palani Oct 21 '13 at 11:10
  • 1
    @AlagesanPalani Have you tried it? Why don't you and see for yourself, you might be surprised. To find out why this works, see http://blogs.msdn.com/b/samng/archive/2008/11/06/dynamic-in-c-iii-a-slight-twist.aspx or http://blogs.u2u.net/kris/post/2010/11/30/Farewell-Visitor.aspx. – Kris Vandermotten Oct 21 '13 at 11:11
  • Yes sir, i tried it, when you call Bar or Bar you will get MemoryStream as answer not the appropriate overloaded method. – Alagesan Palani Oct 21 '13 at 11:16
  • 2
    +1. This sure fixed my problem. It enabled calling the right overload without resorting to ugly tricks like creating a dictionary or using case logic. This is the best answer IMO and it's sad it has no votes. – toddmo Dec 09 '13 at 00:52
  • @toddmo yep, this was exactly what I needed, too. I'd never used dynamic before and was really trying to avoid huge switch states or a lookup class. – Astravagrant Oct 09 '15 at 11:06
  • That's a convenient solution. Just wanna add, that in case of having also generic method overload Foo(T v) where T : struct (to avoid boxing) this approach (http://stackoverflow.com/a/36775837/2164198) can help to eliminate runtime error – Ivan Samygin May 17 '16 at 15:51
  • 2
    Best answer so far. – Skyblade Jun 05 '18 at 15:39
5

Perhaps something like this:

void Bar<T>()
{
   if(typeof(T) == typeof(Stream))
      Foo(default(T) as Stream);  //just to show the scenario
}
Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
Alireza
  • 10,237
  • 6
  • 43
  • 59
  • 3
    Not sure why this is downvoted; it isn't a *great* approach, but that is because the scenario itself isn't ideal - it is about as good an answer as you can get for the question. This is more a reflection on the code in the question than in the answer. – Marc Gravell Oct 21 '13 at 10:52
  • 1
    Actually, I even think that this is the best solution because it is pretty clear what happens. If the number of overloads will not change, this approach is absolutely valid - given this problem. In other scenarios I would also avoid type-based flow control, but this is exactly what has been asked here. – gehho Oct 21 '13 at 11:12
2

This is not a "pretty" answer (indeed, since this is kinda subverting the intent of generics, it is hard to find a pretty answer inside the language), but you could perhaps code the overload lookups via a dictionary:

static readonly Dictionary<Type, Action<object>> overloads
    = new Dictionary<Type, Action<object>> {
        {typeof(Stream), o => Foo((Stream)o)},
        {typeof(MemoryStream), o => Foo((MemoryStream)o)}
    };
public static void Bar<T>() {
    Action<object> overload;
    if (overloads.TryGetValue(typeof(T), out overload)) {
        overload(default(T));
    } else {
        Foo((object)default(T));
    }
}

This isn't nice, and I don't recommend it. For easier maintenance, you could perhaps move the overloads population to a static constructor / type initalizer, and populate it via reflection. Note also that this only works for exact T - it won't work if someone uses an unexpected type (Bar<NetworkStream> for example) - although you could presumably loop over the base-types (but even then, it doesn't have great support for interfaces etc).

This approach doesn't have much to recommend it, all things considered. I would probably advise approaching the entire problem from a different angle (i.e. removing the need to do this).

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • nice answer. One question `Foo((object)default(T))` why to cast to `object`? Having no constraint to generic type `T` at `Bar` signature, I suppose this is redundant here. – Ilya Ivanov Oct 21 '13 at 10:54
  • @IlyaIvanov it is redundant to the compiler; I posit that it is useful to the reader, to make it clear that we know this is always calling the `object` overload – Marc Gravell Oct 21 '13 at 10:56
  • I see, thanks. I also thought about such scenario using dictionary as dispatcher, but it really hearts maintainability. Never thought about populating it with reflection. Anyway, thanks. – Ilya Ivanov Oct 21 '13 at 10:58
0

default(T) will always return null when type T is of reference type and will return zero if T is of numeric value types.

So at any time its not returning an object using which you can call your overloaded version of Foo methods.

So Short answer you cant do this, you have to find out other ways to call overloaded methods.

Alagesan Palani
  • 1,984
  • 4
  • 28
  • 53
  • 1
    The fact that `default(T)` returns `null` is irrelevant. It's true that `null` does not have a runtime type, but `default(T)` certainly does have a compile time type, which is T. This T is used for overload resolution. – Kris Vandermotten Oct 21 '13 at 11:05
  • And when T is unconstrained, it will behave as `object` in the overload resultion algorithm. Overload resolution works at compile time, and runtime types cannot be considered at compile time. This obviously changes when `dynamic` is used: then overload resultion happens at runtime, and (unfortunately for the example posted by OP) does not take into account compile time types. – Kris Vandermotten Oct 21 '13 at 11:24
0

I had the same issue, and the unique solution I knew was try-casting it until I got something other than null. Then, I would have the correct type at compile time and the compiler would know the right overload to call. I could not find another way to achieve this 'run-time polymorphism'.

To avoid using a dictionary or a switch-like solution (poor maintainability,as pointded out by Marc), just call Method((dynamic) o) and DLR will call the correct overload method according to the run-time type.

Just remember to:

1) Provide a default overload with the most top type possible;

2) Watch out for any ambiguity during resolution of type at run-time (i.e. two indepenent interfaces and one implementation that uses both);

3) Handle null case.

You can read more about it here.

Hope I've helped.

Walter Macambira
  • 2,574
  • 19
  • 28