tags:

views:

57

answers:

4

If I have a class as below:

class MyClass<T,U>
{
  public void DoSomething(T t)
  {
  }

  public void DoSomething(U u)
  {
  }
}

But construct it using the same types (new MyClass<int,int>()) This compiles fine, but if I try to call DoSomething it errors because of an ambiguous call, which of course is correct. But what if the method was called through reflection or some other dynamic way. I guess it would throw an exception at run time. So my question is why does the compiler allow me create this class with the same types if there's an exception waiting to happen?

+1  A: 

Well, does it make sense to be able to have T and U be the same type, if you don't want to call DoSomething? If so, wouldn't it be infuriating to be prevented by the compiler for no reason?

In fact, the method could be called with reflection, if you picked the method carefully.

I would say that if the compiler should do anything, it should be warning the author of this class itself (not the user of the class) that the overload could lead to ambiguity in some cases. Frankly overload can cause plenty of problems - anything nudging people away from it in dangerous situations would be a good thing :)

Jon Skeet
+1  A: 

would you rather the compiler didn't allow this? theres infinite cases that are fine, and this one that isnt.

Andrew Bullock
+4  A: 

The compiler is concerned with actual ambiguity and does not highlight any ambiguity related to overloading generic methods.

If you don't want to allow this, unfortunately there's no way to add a Generic Type Constraint, i.e. where T != U, so you'll need to just throw an exception in the constructor:

public MyClass()
{
    if (typeof(T) == typeof(U))
    {
       throw new Exception("T and U cannot be of the same type");
    }
}

What you should do is use different names so that the two method names to not collide with one another. This is the safest way of working in this scenario:

public DoSomethingWithFirst()
{
   // For T
}

public DoSomethingWithSecond()
{
   // For U
}
GenericTypeTea
A: 

By the way, if you don't control the class and you want to allow those methods to be called on MyClass<int, int>, you can do it without reflection and it will succeed at runtime with no exception. For example, if you create a generic method that takes T and U as parameters, then overload resolution within that method can tell them apart:

public static class MyClassExtensions
{
    public static void DoSomethingT<T, U>(this MyClass<T, U> obj, T t)
    {
        obj.DoSomething(t);
    }

    public static void DoSomethingU<T, U>(this MyClass<T, U> obj, U u)
    {
        obj.DoSomething(u);
    }
}

DoSomethingT will choose the overload of DoSomething that takes a T. This is chosen at compile-time and is valid for all values of T or U. You can then call myObj.DoSomethingT(3), with a MyClass<int, int>, and it will call MyClass.DoSomething(T t) without any ambiguity.

(As others have said, if you do control the class then you should just give the methods different names.)

Quartermeister