接口的扩展方法是否被视为优先级低于特定的扩展方法?
我有以下扩展类:
public static class MatcherExtensions { public static ExecMatcher Match(this Tuple item) { return new ExecMatcher(item.Item1, item.Item2); } public static ExecMatcher Match(this ITupleMatchable item) { var tuple = item.PropertiesToMatch; return new ExecMatcher(tuple.Item1, tuple.Item2); } public static ExecMatcher Match(this T item) { return new ExecMatcher(item); } }
如果我创建一个元组并调用Match()
,它会正确使用第一个扩展方法:
var tuple = Tuple.Create(1, "a"); tuple.Match().With(1, "a")... // compiles just fine.
如果我创建一个int并调用Match()
,它会正确使用最后一个扩展方法:
var one = 1; one.Match().With(1)... // compiles just fine.
但是,如果我创建实现ITupleMatchable
并尝试匹配的SomeClass
,编译器仍会选择第三种扩展方法,而不是第二种扩展方法,尽管后者是更具体的匹配:
var obj = new SomeClass(1, "a"); obj.Match().With(1, "a")... // won't compile.
基于Eric Lippert对类似问题的回答 ,我通过将第三个扩展方法放在子目录中的自己的类中来解决这个问题 ,从而创建一个更长的命名空间,因此它与调用代码之间的距离比版本更长特定于ITupleMatchable
。 这对我来说感觉就像是黑客。 有没有更简洁的解决方法?
如果您只是将new SomeClass(1, "a")
为ITupleMatchable
,它将正常工作:
var obj = (ITupleMatchable)new SomeClass(1, "a"); obj.Match().With(1, "a");
请记住,您的obj
变量具有编译时类型的SomeClass
。 编译器可以“更容易地”将实际类与第三种扩展方法(与任何类型兼容)匹配,而不是必须查看SomeClass
的接口实现,然后将其与例如第二种扩展方法进行匹配。
但是如果你提供this
参数作为实际的接口类型,那么第二种扩展方法更适合,因为它正是方法所寻找的类型,而不是更广泛的“任何类型”。 即它是一个更具体的声明,所以“更好”。
请注意,一旦找到候选扩展方法集(通过与命名空间相关的规则等),就会使用正常的重载决策来确定实际方法。 即确定MatcherExtensions
类中至少有一个方法是符合条件的扩展方法,然后编译器会使用正常的重载MatcherExtensions
规则来选择这些方法。 您可以在C#5.0规范的7.5.3
节中找到这些规则。
简而言之:在应用重载决策规则之前(实际上,为了确定哪些方法甚至是合格的),请注意编译器已经确定了generics类型参数。 因此,在评估重载Match(ITupleMatchable
,它会查看Match(SomeClass item)
和Match(ITupleMatchable
。 希望一旦你考虑到这一点,你就会明白为什么,如果变量的类型为SomeClass
,编译器会优先选择第三个扩展名,而反之亦然,如果类型为ITupleMatchable
则反之亦然。