C#方法重载和泛型接口
Posted
技术标签:
【中文标题】C#方法重载和泛型接口【英文标题】:C# Method overloading and generic interface [duplicate] 【发布时间】:2013-03-16 08:36:32 【问题描述】:我对我们项目中的一个问题感到困惑。我试图简化它以重现效果:
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】:当与重载解析结合使用时,这只是类型推断的问题,对您不利。只需明确指定类型参数即可轻松修复 - 不需要强制转换:
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<Bar>
,在另一种情况下,我们从@987654332 转换@到IFoo<Bar>
。根据 C# 5 规范的第 7.5.3.3 节:
给定从表达式 E 转换为类型 T1 的隐式转换 C1,以及从表达式 E 转换为类型 T2 的隐式转换 C2,如果以下至少一项成立,则 C1 是比 C2 更好的转换: - E 具有类型 S 并且存在从 S 到 T1 但不存在从 S 到 T2 的身份转换 - ...
身份转换是从一个类型到自身,是第一次重载的情况,但不是第二次。所以第一次转换比较好。
【讨论】:
我不太确定这是因为类型推断。考虑到它不会抱怨参数不能告诉类型。当第一个 DoTheFoo 方法被删除时,它也可以工作...... @StefanSteinegger:类型推断失败面对重载。整个事情真的很棘手 - 尝试在规范中全部遵循 - 但显然类型推断是问题的部分,否则当你明确指定类型参数时它肯定不起作用。跨度> 这实际上是这个案例中最奇怪的部分。当我指定通用参数时,它可以工作。虽然它能够找到它,但至少在第一个重载被移除时是这样。 我认为编译器试图找到正确重载的顺序一定是个问题。它似乎更喜欢具有通用参数的那个,而不是具有更具体类型的那个。 (IFoo<T>
对我来说似乎比 T
更具体)。这很奇怪。
@StefanSteinegger:IFoo<T>
是否比T
更具体或更具体不是相关问题。方法类型推断成功,然后在成功后的问题是“哪个更好conversion:foo
通过reference转换为IFoo<Bar>
> 转换或foo
通过identity 转换转换为Foo<Bar>
?显然,身份转换获胜,因此重载解析选择Foo<Bar>
方法作为其最佳方法。只有这样才能检查约束,然后我们确定Foo<Bar>
不满足IBar
约束。以上是关于C#方法重载和泛型接口的主要内容,如果未能解决你的问题,请参考以下文章