为什么编译器选择使用IEnumerable覆盖IEnumerable <T>?

Mr *_*son 5 .net c#

考虑以下两种扩展方法:

using System;
using System.Collections.Generic;
using System.Linq;

public static class Extensions
{
    public static bool Contains(this IEnumerable self, object obj)
    {
        foreach (object o in self)
        {
            if (Object.Equals(o, obj))
            {
                return true;
            }
        }
        return false;
    }

    public static bool ContainsEither<T>(this IEnumerable<T> self, T arg1, T arg2)
    {
        return self.Contains(arg1) || self.Contains(arg2);
    }
}
Run Code Online (Sandbox Code Playgroud)

当我编写第二个方法时,我打算调用泛型LINQ Enumerable.Contains<T>方法(从用法推断出类型参数).但是,我发现它实际上是调用第一个方法(我的自定义Contains()扩展方法.当我注释掉我的Contains()方法时,第二个方法编译好,使用该Enumerable.Contains<T>()方法.

我的问题是,为什么编译器选择带有参数的Contains()非泛型IEnumerable参数的方法?我希望它能够选择,因为它比派生更多.Enumerable.Contains<T>()IEnumerable<T>Enumerable.Contains<T>()IEnumerable<T>IEnumerable

更新:感谢Jon Skeet的出色答案.知道设计师选择这种方式的原因会很有趣.我花了一些时间探索这个问题的重要事项,我想到了一个(授予它有点伸手可及)

using System;
using System.Collections.Generic;
using System.Linq;

public static class Extensions
{
    public static bool Contains(this IEnumerable self, object obj)
    {
        foreach (object o in self)
        {
            if (Object.Equals(o, obj))
            {
                return true;
            }
        }
        return false;
    }

    public static bool ContainsEither<T>(this IEnumerable<T> self, T arg1, T arg2)
    {
        return self.Contains(arg1) || self.Contains(arg2);
    }
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 20

我的问题是,为什么编译器选择带有参数的非泛型IEnumerable参数的Contains()方法?Enumerable.Contains<T>()IEnumerable<T>

因为它位于包含调用它的方法的同一名称空间中.在该命名空间内声明的类型实际上优先于在导入的命名空间中声明的类型.

从C#5规范,第7.6.5.2节:

对C的搜索过程如下:

  • 从最近的封闭命名空间声明开始,继续每个封闭的命名空间声明,并以包含的编译单元结束,连续尝试查找一组候选扩展方法:
    • 如果给定的命名空间或编译单元直接包含具有合格扩展方法Mj的非泛型类型声明Ci,则这些扩展方法的集合是候选集.
    • 如果在给定命名空间或编译单元中使用命名空间指令导入的命名空间直接包含具有合格扩展方法Mj的非泛型类型声明Ci,则这些扩展方法的集合是候选集.
  • 如果在任何封闭的名称空间声明或编译单元中找不到候选集,则会发生编译时错误.