I'm confused by a problem we have in our project. I tried to simplify it to reproduce the effect:
interface IBar { }
class Bar : IBar {}
interface IFoo<T> where T : IBar { }
class Foo<T> : IFoo<T> where T : IBar { }
class Class1
{
public void DoTheFoo<T>(T bar) where T : IBar
{}
public void DoTheFoo<T>(IFoo<T> foo) where T : IBar
{}
public void Test()
{
var bar = new Bar();
var foo = new Foo<Bar>();
DoTheFoo(bar); // works
DoTheFoo<Bar>(foo); // works
DoTheFoo((IFoo<Bar>)foo); // works
DoTheFoo(foo); // complains
}
}
To me this looks fine, but the compiler complains on the last call, because it tries to DoTheFoo<T>(T bar)
, instead of DoTheFoo<T>(IFoo<T> foo)
and complains that the argument type does not fit.
DoTheFoo<T>(T bar)
, the last call works!DoTheFoo<T>(Foo<T> foo)
, it works, but I can't use thatIt is not too hard to work around this in our current code. But it is a) strange and b) too bad that we can't have these two overloaded methods.
Is there a common rule that explains this behaviour? Is it possible to make it work (except of giving the methods different names)?
It's just a matter of type inference not quite working in your favour when combined with overload resolution. It's easy to fix by just specifying the type argument explicitly - no cast is required:
DoTheFoo<Bar>(foo);
Usually I'm nervous of overloads which take fairly different parameter types though. Often the code ends up simpler if you just give the methods different names. Aside from anything else, then your readers don't need to try to perform overload resolution at the same time as type inference...
EDIT: I believe the problem is that the ordering works like this:
T = Foo<Bar>
and for the second method we get T = Bar
. Both methods are applicable at this point.T
checked - and that fails because there's no reference conversion from Bar
to IFoo
.There's an Eric Lippert blog post about why the language is designed this way, a blog post I wrote about it, and an article I wrote about overloading in general. Each of them may or may not help :)
EDIT: Leaving type inference aside for a moment, the reason the first method is more specific is that in one case we're converting from Foo<Bar>
to Foo<Bar>
, and in the other we're converting from Foo<Bar>
to IFoo<Bar>
. As per section 7.5.3.3 of the C# 5 specification:
Given an implicit conversion C1 that converts from an expression E to a type T1, and an implicit conversion C2 that converts from an expression E to a type T2, C1 is a better conversion than C2 if at least one of the following holds: - E has a type S and an identity conversion exists from S to T1 but not from S to T2 - ...
The identity conversion is from a type to itself, which is the case for the first overload, but isn't for the second. So the first conversion is better.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With