代码之家  ›  专栏  ›  技术社区  ›  Stefan Steinegger

C#方法重载和通用接口[重复]

  •  8
  • Stefan Steinegger  · 技术社区  · 12 年前

    我对我们项目中的一个问题感到困惑。我试图将其简化以重现效果:

    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巴) ,最后一个呼叫有效!
    • 当我将其更改为 DoTheFoo<T>(Foo<T> foo) ,它有效,但我不能用

    在我们当前的代码中解决这个问题并不难。但a)很奇怪,b)太糟糕了,我们不能有这两个重载方法。

    有没有一个共同的规则可以解释这种行为?有可能让它工作吗(除了给方法起不同的名字)?

    1 回复  |  直到 12 年前
        1
  •  7
  •   Jon Skeet    12 年前

    这只是一个类型推理的问题,当与过载解决方案相结合时,对您不太有利。只需显式指定类型参数就很容易修复-不需要强制转换:

    DoTheFoo<Bar>(foo);
    

    不过,通常我会对重载感到紧张,因为重载采用了相当不同的参数类型。如果只给方法起不同的名字,代码通常会变得更简单。除此之外,你的 读者 不需要在进行类型推理的同时尝试执行重载解析。。。

    编辑:我认为问题在于订购是这样的:

    • 找到两种方法
    • 类型推理应用于这两种方法 而不验证约束 -所以对于第一种方法,我们得到 T = Foo<Bar> 对于第二种方法,我们得到 T = Bar 这两种方法在这一点上都适用。
    • 执行过载解析,这决定第一种方法是最具体的方法。
    • 只有 之后 已执行过载解析是 T 选中-由于没有来自的引用转换,因此失败 Bar IFoo .

    有一个 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<条形图> ,而在另一方面,我们正在从 Foo<条形图> IFoo<Bar> 根据C#5规范第7.5.3.3节:

    给定从表达式E转换为类型T1的隐式转换C1和从表达式E转化为类型T2的隐式转化C2,如果以下至少一项成立,则C1是比C2更好的转化: -E具有类型S,并且存在从S到T1的身份转换,而不是从S到T2的身份转换 - ...

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