C # Method overload and common interface

I am confused by the problem that 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 } } 

This looks great to me, but the compiler complains about the last call because it tries DoTheFoo<T>(T bar) instead of DoTheFoo<T>(IFoo<T> foo) and complains that the argument type is not suitable.

  • When I delete the DoTheFoo<T>(T bar) method, the last call works!
  • When I change it to DoTheFoo<T>(Foo<T> foo) , it works, but I can’t use this

In our current code, it's not too hard to get around this. But this is a) strange and b) too bad that we cannot have these two overloaded methods.

Is there a general rule to explain this behavior? Is it possible to make it work (other than providing methods with different names)?

+8
generics c # method-overloading
source share
1 answer

It's just a matter of type inference that doesn't quite work in your favor when combined with overload resolution. It is easy to fix by simply specifying the type argument explicitly - no casting required:

 DoTheFoo<Bar>(foo); 

I usually get nervous due to overloads that accept quite different types of parameters. Often the code becomes simpler if you just pass methods to other names. Among other things, your readers should not try to perform overload resolution simultaneously with the type of output ...

EDIT: I believe the problem is that ordering works as follows:

  • Both methods found
  • Type inference applies to both methods without checking constraints - therefore, for the first method, we get T = Foo<Bar> , and for the second method we get T = Bar . Both methods are currently applicable.
  • An overload is performed that determines that the first method is the most specific.
  • Only after overload resolution has been performed, the restriction on T noted - and this does not work, because there is no reference to the conversion from Bar to IFoo .

There Eric Lippert on the blog about why the language is designed this way , I wrote about it , and the article I wrote about overload in general . Each of them may or may not help :)

EDIT: Leaving for a while the output of the output method, the reason the first method is more specific is that in one case we go from Foo<Bar> to Foo<Bar> , and in the other from Foo<Bar> to IFoo<Bar> . According to section 7.5.3.3 of the C # 5 specification:

Given the implicit conversion of C1, which is converted from the expression E to type T1 and the implicit conversion of C2, which is converted from the expression E to type T2, C1 is a better conversion than C2 if at least one of the following statements: - E is of type S, and identity transformation exists from S to T1, but not from S to T2 -...

The transformation of identity occurs from type to self, which is the case for the first overload, but not for the second. So, the first conversion is better.

+7
source share

All Articles