Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Generic Types Cause Ambiguity

Tags:

c#

generics

I am creating a custom generic class:

class Widget< T1, T2>
{
    ...
    public bool Bar( T1 type1 )
    {
        ...
    }
    public bool Bar( T2 type2 )
    {
        ...
    }
    ...
}

The following lines, of course, creates an ambiguous call compile error:

Widget<int, int> Foo = new Widget<int, int>();
...
Foo.Bar(5);
...

Is there any way around this? Is there a clause that I can put along the lines of "where : TypeOf(T1) != TypeOf(T2)", or any way to make this disambiguous? Preferably int,int would be available, but it is not manditory.

Update:

I actually on my own discovered an acceptable solution (for me) to this problem, for those who are interested

class Widget< T1, T2>
{
    ...
    public bool Bar( object o )
    {
        if( o.GetType() == typeof(T1) )
        {
            ...
        }
        if( o.GetType() == typeof(T2) )
        {
            ...
        }
    }
    ...
}
like image 538
Steve H. Avatar asked Feb 23 '10 21:02

Steve H.


2 Answers

Is there a clause that I can put along the lines of "where : TypeOf(T1) != TypeOf(T2)"

You could make your constructor throw an exception at runtime. But there's no way to prevent this situation at compile time.

Any way to make this unambiguous?

You should change the names of your methods so that they do not collide. That is by far the safest and easiest thing to do.

In fact, IIRC the CLR reserves the right to fail to create a type that produces an ambiguity in method signatures like that. (Obviously our implementation actually does succeed, but you are treading on very thin ice when you pull these sorts of shenanigans.)

Doing this kind of thing is a really, really bad idea because it can get you into all sorts of trouble. Here's an example of how things go terribly wrong:

http://blogs.msdn.com/ericlippert/archive/2006/04/05/odious-ambiguous-overloads-part-one.aspx

http://blogs.msdn.com/ericlippert/archive/2006/04/06/odious-ambiguous-overloads-part-two.aspx

Also note that the compiler will stop you from creating a type such that it implements two interfaces that could be identical under construction. This is illegal:

class C<T, U> : IFoo<T>, IFoo<U> { ... }

because you could then construct C<int, int> and the CLR would have no way of knowing which methods corresponded to which interface slots.

But I seem to have digressed somewhat. Back on topic.

Since you are the creator of this class, you can choose to rename your "Bar" methods so that they are different under any possible construction. Suppose you stubbornly choose not to. Is there anything that the user of your unfortunate class can do if they want to make Widget<int, int>? Yes, actually, there is, as kvb points out. They can define extension methods that do the right thing.

public static void BarTheFirst<A, B>(this Widget<A, B> w, A a)
{
    w.Bar(a);
}

public static void BarTheFirst<A, B>(this Widget<A, B> w, B b)
{
    w.Bar(b);
}

Overload resolution is done at compile time, and at compile time all we know is that the first one calls the Bar that takes an "A", and the second one calls the Bar that takes a "B". We do not re-do overload resolution at runtime, so now you can say

Widget<int, int> w = whatever;
w.BarTheFirst(5);
w.BarTheSecond(10);

and it will do the right thing.

like image 56
Eric Lippert Avatar answered Sep 21 '22 02:09

Eric Lippert


Give your functions unique names

like image 44
Kerido Avatar answered Sep 24 '22 02:09

Kerido