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问题
- 关于堆栈溢出的讨论
- 关于堆栈溢出的讨论
- 关于堆栈溢出的讨论
静态类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);
}
}