我对项目中存在的问题感到困惑。我试图简化它以重现效果:
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
}
}
对我来说这看起来很好,但编译器在最后一次调用时抱怨,因为它尝试DoTheFoo<T>(T bar)
而不是DoTheFoo<T>(IFoo<T> foo)
并抱怨参数类型不合适。
DoTheFoo<T>(T bar)
时,最后一次通话有效!DoTheFoo<T>(Foo<T> foo)
时,它可以正常工作,但我无法使用在我们当前的代码中解决这个问题并不困难。但它是a)奇怪而且b)太糟糕了,我们不能拥有这两种重载方法。
是否有解释此行为的通用规则?是否可以使它工作(除了给方法不同的名字)?
答案 0 :(得分:7)
与重载决策结合使用时,类型推断的问题并不完全符合您的利益。只需明确指定类型参数即可轻松修复 - 不需要强制转换:
DoTheFoo<Bar>(foo);
通常我对过载采取相当不同的参数类型感到紧张。如果只是给方法赋予不同的名称,代码通常会更简单。除了其他任何东西,那么您的读者不需要尝试在类型推断的同时执行重载解析......
编辑:我认为问题在于订购的工作方式如下:
T = Foo<Bar>
,对于第二个方法,得到T = Bar
。这两种方法都适用。T
已检查的约束 - 但由于没有Bar
到IFoo
的引用转换而失败。< / LI>
有Eric Lippert blog post about why the language is designed this way,blog post I wrote about it和an article I wrote about overloading in general。他们每个人可能会或可能不会帮助:)
编辑:暂时抛弃类型推断,第一种方法更具体的原因是,在一种情况下,我们正在从Foo<Bar>
转换为Foo<Bar>
,而在另一种情况下,我们是从Foo<Bar>
转换为IFoo<Bar>
。根据C#5规范的第7.5.3.3节:
给定从表达式E转换为类型T1的隐式转换C1,以及从表达式E转换为类型T2的隐式转换C2,如果至少下列其中一个成立,则C1是比C2更好的转换: - E具有类型S,并且存在从S到T1而不是从S到T2的标识转换 - ......
身份转换是从类型到自身, 是第一次重载的情况,但不是第二次的。所以第一次转换更好。