扩展方法调用不编译,但对相同代码的静态方法调用编译

Extension method call does not compile, but static method call to same code does compile

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

我从 C# 编译器中收到一个奇怪的错误:

The type 'System.Windows.Forms.Control' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Windows.Forms, Version=4.0.0.0

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

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

//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);
}

所以你看检测使用哪个扩展没有问题。 LeadSystem 是一个枚举,SourceLeadConfiguration 是一个 class。

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

这是对 C# 编译器行为的一致抱怨,使程序员非常疯狂。不幸的是,没有规范的 Q+A,每个案例都不同。它的第一份报告开始出现在 VS2012/.NET 4.5 时间范围内。编译器以前不这样做,它闻起来像是一个影响比预期更大的错误修复。我们也没有经常听到它,大多数程序员只是按照错误消息中的指导添加程序集引用。你也应该这样,它可以轻松解决问题。

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

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

扩展方法很棘手,因为可能有很多,而不仅仅是 SourceLeadConfigurationHelper.GetLeads() 您(显然)希望被选中的方法。程序集 可能 定义其他扩展方法,它们只是 可能 添加更多扩展方法到 SourceLeadConfiguration 类型。如果编译器知道程序集存在,但不知道它长什么样,因此无法知道它可能包含哪些扩展方法,那么它就会报错。请注意,这解释了为什么直接调用静态方法时不会出现错误,编译器不需要去寻找扩展。

您肯定能猜到编译器是如何找到 System.Windows.Forms 程序集的。它是由另一个程序集引入的,您提到但没有描述的那个程序集。诊断是 System.Windows.Forms.Control 类型在该程序集的元数据中泄露。通常作为参数或 return 类型的 public 方法。您必须在源代码中进行一些挖掘才能找到它,而不是一个可以消除它的 slamdunk。


关于 可能 的扩展方法的更多细节,ExtensionAttribute 类型在 .NET 4.5 中从 System.Core.dll 移动到 mscorlib.dll。一个相当大的变化,当程序员没有正确构建他们的程序集时,它会导致很多问题。如果您的目标是 .NET 4.0,那么您需要仔细检查一下,详情 are here.


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