Ilya Ivanov
Ilya Ivanov

Reputation: 23626

How to call a method overload based on closed generic type?

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.

Upvotes: 15

Views: 3992

Answers (6)

Walter Macambira
Walter Macambira

Reputation: 2605

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.

Upvotes: 0

Kris Vandermotten
Kris Vandermotten

Reputation: 10201

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.

Upvotes: 10

Alagesan Palani
Alagesan Palani

Reputation: 2014

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.

Upvotes: 0

Alireza
Alireza

Reputation: 10476

Perhaps something like this:

void Bar<T>()
{
   if(typeof(T) == typeof(Stream))
      Foo(default(T) as Stream);  //just to show the scenario
}

Upvotes: 5

Panagiotis Kanavos
Panagiotis Kanavos

Reputation: 131492

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

Upvotes: 10

Marc Gravell
Marc Gravell

Reputation: 1063013

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).

Upvotes: 3

Related Questions