C# Generic Types Cause Ambiguity
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 : Type开发者_StackOverflow中文版Of(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) )
{
...
}
}
...
}
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.
Give your functions unique names
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.
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)
{
...
}
}
精彩评论