扩展方法优先级

Him*_*ere 4 c# extension-methods

我从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<T> Where<T>(this IEnumerable<T> source, Func<T, bool> predicate)
        {
            foreach (var obj in source)
                if (predicate(obj)) yield return obj;
        }
    };

}
Run Code Online (Sandbox Code Playgroud)

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

Jon*_*eet 11

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

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

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

  • 这取决于“ using”指令是位于“ namespace”声明的内部还是外部。 (2认同)

Jep*_*sen 6

这是一个更长的示例,有五种可能的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<T> Where<T>(this IEnumerable<T> source, Func<T, bool> predicate)
    {
      Console.WriteLine("My own MySuperLinq method");
      return null; // will fix one day
    }
  }
}

namespace Me.MyName
{
  static class Extensions
  {
    public static IEnumerable<T> Where<T>(this IEnumerable<T> source, Func<T, bool> predicate)
    {
      Console.WriteLine("My own MyName method");
      return null; // will fix one day
    }
  }
}

namespace Me
{
  static class Extensions
  {
    public static IEnumerable<T> Where<T>(this IEnumerable<T> source, Func<T, bool> 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<T> Where<T>(this IEnumerable<T> source, Func<T, bool> predicate)
  {
    Console.WriteLine("My own global-namespace method");
    return null; // will fix one day
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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