扩展方法和编译时检查
也许有点棘手,但我想知道为什么。在 System.Core.dll
的 System.Linq.Enumerable.cs
中,我们有:
public static int Count<TSource>(this IEnumerable<TSource> source);
在我的代码中,我正在做一些邪恶的事情:
namespace Test
{
public static class Extensions
{
public static int Count<TSource>(this IEnumerable<TSource> source)
{
return -1; //evil code
}
}
//commented temporarily
//public static class CommentedExtensions
//{
// public static int Count<TSource>(this IEnumerable<TSource> source)
// {
// return -2; //another evil code
// }
//}
public static void Main(string[] args)
{
Console.WriteLine(Enumerable.Range(0,10).Count()); // -1, evil code works
Console.Read();
}
}
如果我取消注释 CommentedExtensions,我会收到一个编译错误,如预期的那样显示“此调用不明确 blabla”。但为什么我第一次没有得到这个错误呢?也很暧昧啊!
编辑 经过另一次测试,我发现如果扩展方法位于不同的命名空间中,即使它们完全相同,也不会出现编译错误。为什么允许?它带来了 c# 中方法调用的不明确性。
EDIT2 我知道事实上这两个 Count
在 IL 中是不同的。事实上,它正在调用
Enumerable.Count(Enumerable.Range(0,10))
,而我的邪恶扩展方法正在调用:
MyExtension.Count(Enumerable.Range(0,10))
所以它们是不同的。但我还是觉得那味道很难闻。我们有“真正的”扩展方法吗?哪些可以阻止邪恶行为?
Maybe a little tricky, but I wonder why. In System.Linq.Enumerable.cs
of System.Core.dll
we have:
public static int Count<TSource>(this IEnumerable<TSource> source);
In my code I'm doing something evil:
namespace Test
{
public static class Extensions
{
public static int Count<TSource>(this IEnumerable<TSource> source)
{
return -1; //evil code
}
}
//commented temporarily
//public static class CommentedExtensions
//{
// public static int Count<TSource>(this IEnumerable<TSource> source)
// {
// return -2; //another evil code
// }
//}
public static void Main(string[] args)
{
Console.WriteLine(Enumerable.Range(0,10).Count()); // -1, evil code works
Console.Read();
}
}
If I uncomment CommentedExtensions
, I'll get a compile error saying "this call is ambiguous blabla" as expected. But why I didn't get this error at the first time? It's also ambiguous!
EDIT After another test, I found that I won't get compile errors if the extension methods are in different namespaces, even they are completely the same. Why it's allowed? It brings ambiguous call of methods in c#.
EDIT2 I know in fact the two Count
are different in IL. In fact it's calling
Enumerable.Count(Enumerable.Range(0,10))
and my evil extension method is calling:
MyExtension.Count(Enumerable.Range(0,10))
so they are different. But still I think it's a bad smell. Do we have "real" extension methods? which can prevent the evil behavior?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
C# 语言规范 的第 7.6.5.2 节介绍了编译器如何确定哪个扩展方法在范围内,以及哪些扩展方法优先于其他方法:
这意味着,如果您的扩展方法与调用它们的代码位于同一名称空间中,则会选择这些扩展方法。将选择封闭命名空间中的扩展方法,而不是已导入的其他命名空间。
Section 7.6.5.2 of the C# language specification describes how the compiler determines which extension methods are in scope, and which extension methods take precedence over others :
Meaning that if you have extension methods in the same namespace than the code from which you call them, these extension methods are selected. Extension methods in an enclosing namespace will be choosed over other namespaces that have been imported.
看来 C# 首先在当前命名空间中查找
在本例中,IL 是
如果我将 main 方法移至另一个命名空间 (XXX),在这种情况下,编译器会将该方法解析为 System.Linq 版本
要在后一个示例中显式使用您的方法,请使用
It appears the C# looks in current name space first
In this example the IL is
If I move the main method into the another namespace (XXX) in this case the compiler resolves the method to the System.Linq version
To explicitly use your method in the latter example you use
如果您创建一个新类并将 using 添加到两个命名空间,然后使用在两个命名空间中定义的方法,则错误应该会再次出现。
扩展方法是通过命名空间来区分的,而不是通过声明它们的静态类来区分的。如果在同一命名空间中定义了两个扩展方法,则编译器可以知道采用哪两个方法。
If you create a new class and add usings to both namespaces and then use your method that is defined in both namespaces, the error should be there again.
Extension methods are distinguish by namespaces, not by the static classes they are declared in. The compiler can know which one two take if two extension methods are defined in the same namespace.
我认为,您正在编写两个具有相同重载的方法,这违反了 OOP 的原则。
如果扩展方法与您使用的扩展方法位于相同的命名空间范围内,那么如果相同的扩展方法存在于 System.Core.dll 中,则它将优先于 System.Core.dll 方法(因为它是在使用位置找到的最接近的重载)。两个命名空间。
为了证明这一点,请将扩展方法名称更改为 MyCount1(...) & MyCount2(...) ,那么它应该适合你。
I think, you are writing Two methods with the same overload which is against the principles of OOPs.
If extension method is within the same namespace scope as that of your usage, then it will take precedence(since it is the closest overload found at the place of usage) over the System.Core.dll one, if the same extension methods exists in both namespaces.
To prove this, change your extension method name to MyCount1(...) & MyCount2(...) , then it should work for you.