C#方法重载和通用接口

时间:2013-03-26 13:58:22

标签: c# generics overloading

我对项目中存在的问题感到困惑。我试图简化它以重现效果:

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)太糟糕了,我们不能拥有这两种重载方法。

是否有解释此行为的通用规则?是否可以使它工作(除了给方法不同的名字)?

1 个答案:

答案 0 :(得分:7)

与重载决策结合使用时,类型推断的问题并不完全符合您的利益。只需明确指定类型参数即可轻松修复 - 不需要强制转换:

DoTheFoo<Bar>(foo);

通常我对过载采取相当不同的参数类型感到紧张。如果只是给方法赋予不同的名称,代码通常会更简单。除了其他任何东西,那么您的读者不需要尝试在类型推断的同时执行重载解析......

编辑:我认为问题在于订购的工作方式如下:

  • 找到两种方法
  • 类型推断应用于两个方法而不验证约束 - 因此对于第一个方法,我们得到T = Foo<Bar>,对于第二个方法,得到T = Bar。这两种方法都适用。
  • 执行过载分辨率,决定第一种方法是最具体的方法。
  • 执行重载解析后,仅T已检查的约束 - 但由于没有BarIFoo的引用转换而失败。< / LI>

Eric Lippert blog post about why the language is designed this wayblog post I wrote about itan 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的标识转换    - ......

身份转换是从类型到自身, 是第一次重载的情况,但不是第二次的。所以第一次转换更好。