扩展方法优先级

我从https://msdn.microsoft.com/en-us/library/vstudio/bb383977.aspx上读到,基本类型上具有相同名称和签名的扩展方法永远不会被调用,但是“覆盖”了什么呢?扩展方法本身:

using System; using System.Linq; using System.Collections.Generic; namespace ConsoleApplication1 { public class Program { public static void Main(string[] args) { var query = (new[] { "Hans", "Birgit" }).Where(x => x == "Hans"); foreach (var o in query) Console.WriteLine(o); } } public static class MyClass { public static IEnumerable Where(this IEnumerable source, Func predicate) { foreach (var obj in source) if (predicate(obj)) yield return obj; } }; } 

当我调试这个程序时,我会遇到我自己的扩展方法而不是System.Linq提供的那个(尽管包含了名称空间)。 我是否遗漏了任何内容,或者扩展方法是否优先考虑?

当编译器搜索扩展方法时,它从在与调用代码相同的命名空间中的类中声明的那些方法开始,然后向外工作直到它到达全局命名空间。 因此,如果您的代码位于名称空间Foo.Bar.Baz ,它将首先搜索Foo.Bar.Baz ,然后搜索Foo.Bar ,然后搜索Foo ,然后搜索全局名称空间。 一旦找到任何符合条件的扩展方法,它就会停止。 如果它在同一步骤中找到多个符合条件的扩展方法,并且没有一个比另一个“更好”(使用正常的重载规则),那么您将得到模糊性的编译时错误。

然后 (如果它没有找到任何东西)它考虑using指令导入的扩展方法。 因此,如果将扩展方法移动到与您的扩展方法无关的其他命名空间,则由于模糊性(如果using指令导入命名空间),您将获得编译时错误,或者只能找到System.Linq方法(如果没有导入包含方法的命名空间)。

这在C#规范的7.6.5.2节中规定。

以下是五个可能的Where<>方法的较长示例:

 using System; using System.Collections.Generic; using System.Linq; // **OUTSIDE** namespace Me.MyName { using System.MySuperLinq; // **INSIDE** static class Test { static void Main() { (new[] { "Hans", "Birgit" }).Where(x => x == "Hans"); } } } namespace System.MySuperLinq { static class Extensions { public static IEnumerable Where(this IEnumerable source, Func predicate) { Console.WriteLine("My own MySuperLinq method"); return null; // will fix one day } } } namespace Me.MyName { static class Extensions { public static IEnumerable Where(this IEnumerable source, Func predicate) { Console.WriteLine("My own MyName method"); return null; // will fix one day } } } namespace Me { static class Extensions { public static IEnumerable Where(this IEnumerable source, Func predicate) { Console.WriteLine("My own Me method"); return null; // will fix one day } } } // this is not inside any namespace declaration static class Extensions { public static IEnumerable Where(this IEnumerable source, Func predicate) { Console.WriteLine("My own global-namespace method"); return null; // will fix one day } } 

尝试连续删除首选的方法,以查看C#编译器使用的“优先级”。

  • C#编译器将首先在Me.MyName命名空间内搜索静态类,因为它是“当前”命名空间。
  • 然后它将在System.MySuperLinq搜索,因为它在内部using
  • 然后它会出一个级别并在Me命名空间中搜索
  • 然后它将在全局( null )命名空间中进行搜索。
  • 最后,它将搜索SystemSystem.Collections.GenericSystem.Linq

如果在一个“级别”(上面的项目符号)中找到两个或多个同等相关的方法,那就是编译时的歧义(不会编译)。