C#编译器选择了错误的扩展方法

C#编译器选择了错误的扩展方法,c#,.net,C#,.net,考虑以下代码: using System.Linq; namespace ExtensionMethodIssue { static class Program { static void Main(string[] args) { var a = new[] { 1 }; var b = new[] { 1, 2 }.Where(a.Contains).ToList();

考虑以下代码:

using System.Linq;

namespace ExtensionMethodIssue
{
    static class Program
    {
        static void Main(string[] args)
        {
            var a = new[] { 1 };
            var b = new[] { 1, 2 }.Where(a.Contains).ToList();
            var c = new[] { 1, 2 }.Where(i => a.Contains(i)).ToList();
        }
    }
}
代码编译成功。 然后我添加了nuget包“itext7 7.0.4”,现在编译失败,原因是:

//Error CS0122: 'KernelExtensions.Contains<TKey, TValue>(IDictionary<TKey, TValue>, TKey)' is inaccessible due to its protection level
var b = new[] { 1, 2, 3 }.Where(a.Contains).ToList();

// This is still ok.
var c = new[] { 1, 2, 3 }.Where(i => a.Contains(i)).ToList();
//错误CS0122:“KernelExtensions.Contains(IDictionary,TKey)”由于其保护级别而不可访问
var b=new[]{1,2,3}.Where(a.Contains.ToList();
//这还可以。
var c=new[]{1,2,3}。其中(i=>a.Contains(i)).ToList();
原因是itext7库有一个内部类,该类在全局命名空间()中具有扩展方法

内部静态类内核扩展{
公共静态bool包含(此IDictionary dictionary,TKey){
返回dictionary.ContainsKey(key);
}
}
出于某种原因,编译器选择了具有全局命名空间中不兼容签名的不可访问扩展方法,而不是具有LINQ命名空间中兼容签名的可访问扩展方法


问题是:这种行为是语言规范所期望的,还是编译器中的一个bug?为什么它只在使用“方法组”的情况下失败,并且仍然使用
i=>a.Contains(i)

这是一个编译器错误,不可访问的函数不应该影响重载解析

一般的解决方法是使用lambdas作为参数而不是方法组,因为重载解析似乎可以很好地使用它们。在您的特定场景中,哪种更快/更高效并不明显,根据需要使用相关性能指标进行微优化

在这种特殊情况下,您还可以使用其他扩展方法,例如,如果您正在处理集合,并且不关心重复项或简单循环

有关更多信息,请查看:

  • GitHub上的Roslyn问题
  • 关于堆栈溢出的讨论
  • 关于堆栈溢出的讨论
  • 关于堆栈溢出的讨论

Method Group,持续赠送的礼物。这显然是一个又大又臭的bug——在项目本身中添加一个带有扩展方法签名的类是无法复制的,因此它是双重痛苦的(因为另一个程序集的内部应该是您最不关心的)。哦,实际上,它变得更好了——如果您声明自己的
静态类Foo{public static bool Contains(此IDictionary dictionary,TKey)=>抛出新的NotImplementedException();},问题就会消失(不,不调用此方法,它只是修复重载解析)。这个类的可见性或名称空间似乎并不重要。啊,不,附录:如果该方法是可访问的(
public
internal
),那么一切都很好。但是,如果它是
private
,编译器会抱怨
Foo.Contains
不可访问。这可能提供了一个跨程序集工作的线索,因为另一个程序集的
internal
方法同样是不可访问的——我怀疑这里存在愚蠢的一致性。我认为,
包含
这一事实根本不是一个bug,因为方法组是如何工作的(最初不考虑类型)。即使是
静态bool-Contains(这个bool-x)
也会被考虑。显式类型参数掩盖了这个问题,因为这两个版本有不同数量的类型参数:
a.Contains
我在GitHub上的Roslyn问题中描述了您的案例。
internal static class KernelExtensions {
    public static bool Contains<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key) {
        return dictionary.ContainsKey(key);
}
}