C# 扩展方法调用不会编译,但对同一代码的静态方法调用会编译

C# 扩展方法调用不会编译,但对同一代码的静态方法调用会编译,c#,compiler-errors,extension-methods,C#,Compiler Errors,Extension Methods,有一个库A使用C#扩展方法调用库B 我从C#编译器那里得到了一个奇怪的错误: 类型“System.Windows.Forms.Control”是在以下程序集中定义的 未引用。必须添加对程序集的引用 'System.Windows.Forms,版本=4.0.0.0 库A或B都不依赖于System.Windows.Forms.Control,也不依赖于System.Windows.Forms.ControlSystem.Windows.Forms.Control仅从同一解决方案中的另一个项目引用 奇

有一个库A使用C#扩展方法调用库B

我从C#编译器那里得到了一个奇怪的错误:

类型“System.Windows.Forms.Control”是在以下程序集中定义的 未引用。必须添加对程序集的引用 'System.Windows.Forms,版本=4.0.0.0

库A或B都不依赖于
System.Windows.Forms.Control
,也不依赖于
System.Windows.Forms.Control
System.Windows.Forms.Control
仅从同一解决方案中的另一个项目引用

奇怪的是,如果我将调用语法更改为static方法,它就会成功编译

//static method syntax works fine
var leads = SourceLeadConfigurationHelper.GetLeads(enLeadSystem);

//extension method syntax cause error
//error The type 'System.Windows.Forms.Control' is defined in an assembly that is not referenced. 
var leads = enLeadSystem.GetLeads();
扩展方法如下所示:

public static class SourceLeadConfigurationHelper
{      
    public static IList<ChannelID> GetLeads(this LeadSystem leadSystem);
    public static IList<ChannelID> GetLeads(this SourceLeadConfiguration slc);
    public static IList<ChannelID> GetLeads(LeadSystem leadSystem, bool throwException);
}
公共静态类SourceLeadConfigurationHelper
{      
公共静态IList GetLeads(此LeadSystem LeadSystem);
公共静态IList GetLeads(此SourceLeadConfiguration slc);
公共静态IList GetLeads(LeadSystem LeadSystem,bool throweException);
}
因此,您可以看到,检测要使用的扩展没有问题
LeadSystem
是一个枚举,
SourceLeadConfiguration
是一个类


我有Visual Studio 2013更新版5、.NET Framework 4.0。

这是一个关于C#编译器行为的非常一致的抱怨,让程序员非常疯狂。不幸的是,它没有标准的Q+A,每种情况都是不同的。关于它的第一批报告开始出现在VS2012/.NET 4.5的时间范围内。编译器不习惯这样做,它闻起来像是一个bug修复,其影响比预期的更大。我们也没有经常听说它,大多数程序员只是按照错误消息中的指导添加程序集引用。因此,你应该知道,它很容易解决问题

试着在这里描述一下。它与扩展方法没有直接关系,这种行为特定于方法重载解析。扩展方法只是它的一个特例,当然是一个棘手的例子

查找方法重载匹配并不特别棘手,当重载不明确时,它会生成一条适当的错误消息,这就是问题所在。对于改变的行为,有一点非常清楚,那就是编译器现在更彻底了。它坚持要知道关于争论类型的一切。即使程序员非常清楚,传递的参数不可能与另一个未引用程序集中的类型匹配。但是编译器对此很固执,如果它不知道类型,那么它会坚持让您添加引用

扩展方法很棘手,因为可能有很多方法,而不仅仅是SourceLeadConfigurationHelper.GetLeads(),您(显然)希望选择的方法。程序集可能定义其他扩展方法,它们可能只是向SourceLeadConfiguration类型添加更多扩展方法。如果编译器知道程序集存在,但不知道它看起来像什么,因此不知道它可能包含哪些扩展方法,那么它会抱怨。请注意,这解释了当您直接调用静态方法时为什么没有得到错误,编译器不需要查找扩展

您肯定可以猜到编译器是如何发现System.Windows.Forms程序集的。它是由另一个大会介绍的,你提到的,但没有描述。诊断是System.Windows.Forms.Control类型在该程序集的元数据中泄漏。通常作为公共方法的参数或返回类型。您必须在源代码中进行一些挖掘才能找到它,否则您将无法消除它


关于扩展方法的更多细节可能与此相关,ExtensionAttribute类型在.NET 4.5中从System.Core.dll移动到了mscorlib.dll。这是一个相当大的变化,当程序员没有正确地构建他们的程序集时,它会导致很多问题。如果您的目标是.NET4.0,那么您将需要再次检查此详细信息



希望您能从引用Winforms的程序集中找到它。如果没有,或者您无法消除暴露,那么添加引用确实是让编译器满意的唯一方法。

您重建了所有程序集吗?也许你的定义已经过时了。是的,我清理了所有东西,尝试了两台不同的计算机。可能与你的namspaces有关?但是为什么它在那一行更改后编译而不更改名称空间?你能删除除“public static IList GetLeads(this LeadSystem LeadSystem);”之外的所有其他方法吗再试一次?我以为会是这样。但我仍然无法了解编译器是如何这样工作的。我搜索了所有表达式“this SourceLeadConfiguration”,但所有查找结果都在库中,没有任何Winforms引用(甚至没有子引用)。我想查找“此SourceLeadConfiguration”就足够了。不是吗?不,你没明白。您错过了编译器希望了解其遇到的所有程序集的说明。这些程序集可能包含扩展方法。所以这与SourceLeadConfiguration无关,你必须追逐“控制”,希望我能理解。编译器假定System.Windows.Forms.dll可以包含此类型的另一个扩展,但编译器找不到System.Windows.Forms.dll来检查它。我说得对吗?