tags:

views:

189

answers:

4
+5  A: 

Give your functions unique names

Kerido
+15  A: 

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.

Eric Lippert
"In fact, IIRC the CLR reserves the right to fail to create a type that produces an ambiguity in method signatures like that." Seriously? If that's the case, shouldn't the C# compiler spit out a warning (at least) when you create `Widget<int,int>`?
Mehrdad Afshari
Well, it does: see OP: "ambiguous call compile error"
@wwosik: That error is for the method call. Eric is talking about CLR blowing up the program at the time such a type is **created**. Actually, when I think about it, it's not easy for the compiler to figure out that when it's deeply nested in generic methods in say, external assemblies.
Mehrdad Afshari
@Mehrdad: at one point the design of the language made it illegal to create a generic type that could *possibly* be constructed so as to produce *any* signature collision. Which meant that class C<T> { static C<T> Factory(T t) {...} static C<T> Factory(SerializedState s) { ... } } would be illegal because T could be SerializedState! We abandoned that early on, though late enough in the process that the C# 2.0 bound spec actually says that. I agree it would be nice to produce a warning in these cases, but in practice they don't arise that often.
Eric Lippert
A: 

This looks like a case where you would want a base generic class

abstract class WidgetBase<T1>
{

    public abstract bool Bar(T1 type);
    ... 
}

Then inherit for your implementation

class WidgetA : WidgetBase<int>
{
     public bool Bar(int type)
     {
         ...
     }
}

class WidgetB : WidgetBase<int>
{
     public bool Bar(int type)
     {
         ...
     }
}
Nate Heinrich
A: 

I agree with others that you should change your class so that the collision doesn't occur (e.g. by renaming the methods). However, if you don't own the class, there is a workaround:

public static class WidgetExtensions
{
    public static bool Bar1<T1,T2>(this Widget<T1, T2> w, T1 t1) { return w.Bar(t1); }
    public static bool Bar2<T1,T2>(this Widget<T1, T2> w, T2 t2) { return w.Bar(t2); }
}

You can use these extension methods to call the proper Bar overload. The static methods don't need to be extension methods, but I think it makes it a bit clearer.

kvb