C#扩展方法重载导致“缺少程序集引用”错误

4

这里有一个相关的VS开发票据 https://connect.microsoft.com/VisualStudio/feedback/details/817276/error-cs0012-the-type-is-defined-in-an-assembly-that-is-not-referenced-issued-for-an-extension-method-that-is-not-used

我有两个扩展方法:

public static class ExtensionMethods
{
    public static string GetClientIpAddress(this HttpRequestBase request)
    {
        // ...
    }

    public static string GetClientIpAddress(this HttpRequestMessage request)
    {
        // ...
    }
}

HttpRequestMessage位于System.Net.Http程序集中,而HttpRequestBase位于System.Web(即不同的程序集)中。类ExtensionMethods位于叫做ProjectA的项目中。

该项目编译良好,没有问题。

然后我从另一个项目(比如ProjectB)中使用第一个方法GetClientIpAddress(this HttpRequestBase request),用法如下:

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
    base.OnActionExecuting(filterContext);
    var sessionContext = DependencyResolver.Current.GetService<ISessionContext>();

    // Call to GetClientIpAddress
    sessionContext.ClientIpAddress =
        filterContext.HttpContext.Request.GetClientIpAddress();
}

该项目B已经引用了System.Web,但是当我尝试编译它时,会导致编译器错误:
“类型'System.Net.Http.HttpRequestMessage'在未引用的程序集中定义。您必须添加对程序集'System.Net.Http,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a'的引用。”
我不明白的是为什么我要添加对System.Net.Http的引用。
似乎编译器试图使用第二个方法GetClientIpAddress(this HttpRequestMessage request),这导致缺少对程序集的引用。这是一个bug吗?
当我重命名第一个方法(即摆脱重载)时,一切都可以成功编译。

3
只有当编译器可以看到所有类型的定义时,它才知道你打算调用第一个方法。它需要看到所有类型以执行重载决策。 - Damien_The_Unbeliever
4
你忘记记录你的.NET框架目标了。但很有可能你选择了4.0版本并不适当地在你的EXE项目中使用了“客户端配置文件”(Client Profile)。请将它更改为完整版本,即也包括System.Web的那个版本。 - Hans Passant
哦,有趣。我不知道版本控制在框架和语言之间有所不同。修改一下注释。 - Alex
1
似乎编译器试图使用第二个方法... 不,正如我在第一个评论中所提到的,它甚至还没有进行那样的操作。它在您的代码中找到了一个符号GetClientIpAddress。现在需要确定您打算调用哪个方法。它找到了两个候选方法。现在需要确定应该调用其中哪一个。为了做到这一点,它需要理解两个方法的所有参数类型,以确定最佳的方法,或者如果调用存在歧义等。如果它不能理解第二个方法的参数类型,就无法做到这一点。 - Damien_The_Unbeliever
两个扩展方法都在作用域内,因此编译器必须评估它们。如果您从不同的项目中使用这些扩展,并且某些项目与缺少的引用无关,则拆分静态类并将其部分放入不同的命名空间中。这里有一个解释:https://connect.microsoft.com/VisualStudio/feedback/details/817276/error-cs0012-the-type-is-defined-in-an-assembly-that-is-not-referenced-issued-for-an-extension-method-that-is-not-used - Thomas Eyde
显示剩余4条评论
2个回答

7
从C#5.0规范第7.5.3节中得知:
给定适用的候选函数成员集合,定位最佳函数成员。如果集合只包含一个函数成员,则该函数成员是最佳函数成员。否则,最佳函数成员是与给定参数列表相比其他所有函数成员都更好的那个函数成员,前提是使用§7.5.3.2中的规则将每个函数成员与所有其他函数成员进行比较。
第7.5.3.2节:
给定带有参数表达式集合 { E1, E2,... EN } 和具有参数类型 { P1,P2,... PN } 和 { Q1,Q2,... QN } 的两个适用函数成员 M P和M Q,如果满足以下条件,则定义M P比M Q更好:
• 对于每个参数,从 E X 到 Q X 的隐式转换不比从 E X 到 P X 的隐式转换更好,并且
• 对于至少一个参数,从 E X 到 P X 的转换优于从 E X 到 Q X 的转换。
没有规则“如果参数类型恰好匹配参数类型,请选择该函数” - 因此编译器需要关于所有参数类型的完整类型信息才能评估上述规则。
为了解决问题而不必添加对System.Net.Http的引用?您已经找到了答案 - 使用不同的方法名称。这使您成功,因为7.5.3的先前引用部分如下:
如果集合只包含一个函数成员,则该函数成员是最佳函数成员

是的,我同意“...因此编译器需要关于所有参数类型的完整类型信息,以便能够评估上述规则。” 我认为它已经拥有了所有必要的信息,因为ProjectA已经引用了System.Net.Http,编译器可以使用它来解析参数类型。是吗? - neleus
@neleus - 不,引用不是可传递的。当编译项目B时,它只考虑项目B的代码及其引用。 - Damien_The_Unbeliever
现在我明白了,感谢您详细的描述。在这种情况下,我建议微软改进他们的编译器,这将是一个非常好的功能。 - neleus

0

我也进行了一个简单的测试,没有使用扩展方法,得到了相同的效果。正如Damien_The_Unbeliever所说,这个问题只与方法重载有关。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接