解决扩展方法/LINQ歧义问题

31

我正在为 ReSharper 4 编写一个插件。为此,我需要引用几个ReSharper的程序集之一 (JetBrains.Platform.ReSharper.Util.dll) 。这个程序集包含了一个 System.Linq 命名空间, 其中包含了 System.Core 已经提供的一部分扩展方法。

当我编辑代码时,会产生这些扩展方法之间的歧义,因此我无法使用 OrderBy 等方法。如何解决这个问题?我想使用核心的 LINQ 扩展方法,而不是 ReSharper 提供的。

在尝试编译时,我得到以下错误:

The call is ambiguous between the following methods or properties: 'System.Linq.Enumerable.OrderBy<string,int>(System.Collections.Generic.IEnumerable<string>, System.Func<string,int>)' and 'System.Linq.Enumerable.OrderBy<string,int>(System.Collections.Generic.IEnumerable<string>, System.Func<string,int>)'

编辑: 我尝试了下面的建议,但不幸的是没有成功。与此同时,我通过删除对System.Core的引用 "解决了"这个问题。这样我就可以使用 ReSharper DLL 文件提供的扩展方法。

上传了一个示例程序,其中仅导入了我需要的 ReSharper DLL 文件。我将 System.Core 的别名更改为 SystemCore,添加了 extern alias 指令,但仍然无法解决问题。如果我漏掉了什么,请告诉我。 P.S. 引用是指安装在默认目录下 "C:\Program Files\JetBrains\ReSharper\v4.1\..." 中的 ReSharper v4.1 DLL 文件。


这是文件的顶部吗?你的命名空间是否是"System.Linq.(something)"?同样,一个简短但完整的代码片段可以帮助我们复制和粘贴。 - Jon Skeet
使用 SystemCore::System.Linq 被禁用并不令人鼓舞,我们肯定需要了解更多信息。 - Jon Skeet
好的,现在已经复制了。正在处理中... - Jon Skeet
我想指出这个问题只与 .Net 4 相关。在 .Net 4 下目前还没有一个可接受的解决方案。 - Dmitri Nesteruk
1
现在可以选择正确的解决方案吗? - John K
10个回答

44

这可能是那些很少用到的情况之一,使用extern alias 是有意义的。

在 System.Core 的引用属性页中(即在“引用”下选择 System.Core,右键单击并选择“属性”),将“别名”值更改为“global,SystemCore”(如果一开始为空,则只需使用“SystemCore”)。

然后在你的代码中编写:

extern alias SystemCore;
using SystemCore::System.Linq;

这将使得System.Core.dll中System.Linq名称空间中的所有相关类型等可用。这里的"SystemCore"名称是任意的 - 如果对您更清晰,您可以将其命名为"DotNet"或其他名称。


我上传了一个带问题的小程序。我知道你自己使用 ReSharper,Jon,所以你可能有所需的 DLL 引用。再次感谢! :) - Igal Tabachnik
1
顺便说一下,在 .Net 4.0 中这完全失败了。 - Dmitri Nesteruk
我怀疑C#编译器存在一个bug,导致它忽略了"extern alias"前缀。例如,"using SystemCore::System.Linq"实际上会导入所有System.Linq命名空间的扩展方法,而不仅仅是SystemCore中的那个。 - Qwertie
哦,我看到我的怀疑在下面的答案中得到了证实。除了这个 bug 的工作方式与我想象的不同。在我的情况下,我添加了对 LinqBridge.dll 的引用,并给它起了别名“linqbridge”,但是“using System.Linq”导入了 linqbridge 的扩展方法,尽管在“using”语句中缺少前缀。 - Qwertie
1
还有一个错误是在包含XAML文件的程序集中使用别名。https://connect.microsoft.com/VisualStudio/feedback/details/615953/reference-aliases-are-ignored-on-projects-containing-some-xaml-files - Cameron MacFarland
显示剩余5条评论

7
这并不是一个答案,但也许可以提供一种更容易让其他人从命令行重现此问题的方式(如果你愿意,也可以在 Visual Studio 中使用两个项目来完成这个过程)。
1)创建 BadLinq.cs 并将其构建为 BadLinq.dll:
using System.Collections.Generic;

namespace System.Linq
{
    public static class Enumerable
    {
        public static IEnumerable<T> Where<T>(this IEnumerable<T> source, 
                                              Func<T,bool> predicate)
        {
            return null;
        }
    }
}

2)创建Test.cs:

extern alias SystemCore;

using System;
using SystemCore::System.Linq;

static class Test
{
    static void Main()
    {
        var names = new[] { "Larry", "Curly", "Moe" };

        var result = names.Where(x => x.Length > 1);
    }
}

3) 编译 Test.cs 并指定 extern alias:

csc Test.cs /r:BadLinq.dll /r:SystemCore=System.Core.dll

出现错误:

Test.cs(11,28): error CS1061: 'System.Array' 没有包含 'Where' 的定义,也没有 找到可接受类型为 'System.Array' 的第一个参数的扩展方法 'Where' (您是否缺少 using 指令或程序集引用?)

如果更改为不尝试使用扩展方法(即 Enumerable.Where),则可以在 extern alias 中正常工作。

我认为这可能是编译器的错误。我已经给 C# 团队发送了一封私人邮件列表 - 当我收到回复时,我会更新此答案或添加新的答案。


嗨,Jon。我在好奇地想知道你有没有得到答案? - Igal Tabachnik
很抱歉,我还没有收到关于这个问题的回复。 - Jon Skeet
自从您发布这个问题已经过去两年了,您是否找到了原因为什么它不起作用?我偶然发现了这个问题,现在非常好奇。 - Kevin Cathcart
@Kevin:不,但我很高兴地说,我刚刚用C# 4编译器测试过了,它可以工作 :) - Jon Skeet
有人报告了这个问题(遗憾的是在C# 3/VS 2008中没有修复):https://connect.microsoft.com/VisualStudio/feedback/details/510113/methods-disambiguated-with-extern-alias-cannot-be-invoked-as-extension-methods - Qwertie

2
为了使ReSharper与各种解决方案兼容,它是基于.NET 2.0构建的。LINQ等功能是在C# 3.0中引入的,因此在该框架版本中不可用。因此,JetBrains添加了自己的版本。
解决方案是将您的插件也构建为.NET 2.0。

2

现在这已经不是问题了,因为我可以使用由ReSharper DLL文件提供的LINQ扩展,即使针对.NET 3.0。

Skeet先生又一次是正确的!我能够在项目属性中针对.NET 3.0使用完整的LINQ语法,而不引用System.Core!


你应该能够使用LINQ语法,因为它只是由编译器翻译。当您尝试使用查询表达式时会发生什么? - Jon Skeet
嘿,Jon。 看来你又是对的了!一如既往;) 我以为这是ReSharper的问题,但它不是!LINQ语法可行,System.Core未被引用,DLL针对C# 3.0 :) - Igal Tabachnik
你的意思是针对.NET 3.0吗?顺便问一下,你不会针对语言版本。 - Jon Skeet
糟糕!我是指项目属性中的目标框架。我想我还没有完全清醒 :) - Igal Tabachnik
1
@Igal - 在您的问题中,您提到没有任何帮助,而且我看到您接受了自己的答案作为解决方案。您能否解释一下您是如何真正解决它的,而不是删除system.core.dll呢?谢谢。 - ephraim

1
我曾经遇到了使用 System.ComponentModel 时出现的模糊引用问题。 Visual Studio 报错说 DLL 文件在 v2 和 v4 中都存在。我通过移除对 System DLL 文件的引用并重新添加来解决了这个问题。

这对我很有帮助。对于未来的读者:在我的情况下,DynamicQueryable同时存在于EF扩展和Linq.Dynamic中。 - SHM

0
一个解决办法是将所有代码移至一个使用ReSharper代码的部分类中。在该类中,只导入ReSharper命名空间而不导入System.Core。
在其他部分的部分类中,你会导入所有其他需要的命名空间,包括System.Core,但不包括ReSharper命名空间。

感谢您的建议,但是歧义出现在汇编级别,而不是特定的类上,因此您的建议很遗憾并不能解决它。 - Igal Tabachnik

0

这真的是一个编译器错误。

我曾经遇到过同样的问题,只需清理并重新构建项目即可解决。之后问题就消失了。


是的,可能是这样,但我认为一个9k声望值的用户会想出来。:) 在更复杂的代码(>30k LOC)中,错误确实可能意味着某些东西。 - csomakk

0

我曾经遇到过同样的问题,即使使用了extern alias,我也在Connect上将其作为编译器错误进行了反馈。暂时的解决方法是放弃扩展方法语法。

这个错误已经在Visual Studio 2010中得到修复。


0

我曾经遇到过类似的情况。在苦苦挣扎了两个小时后,我意识到我的库中存在重复的命名空间名称。如果您正在使用由Microsoft发布的Dynamic.cs文件,则唯一需要做的就是将当前命名空间重命名为其他名称,问题就会得到解决。

//Copyright (C) Microsoft Corporation.  All rights reserved.

using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;

namespace System.Linq.Dynamic    <- for example to Linq.Dynamic
{

0

我在使用PagedList在MVC(.Net 4.5, MVC 5)时发现了同样的歧义。我发现,如果我先将模糊不清的参数对象显式转换一下,问题就解决了。如果参数对象是System.Linq.Enumerable和System.Collections.Generic.IEnumerable中的一个方法所引起的歧义,并且源类型是System.Collections.Generic.IEnumerable,那么我就不使用它的扩展方法,而是将其强制转换。在这个例子中,我的存储库方法返回一个List:

searchRequest.CaseSearchResults = csr.SelectMatchingCases(searchRequest);
var results = searchRequest.CaseSearchResults.AsEnumerable<CaseSearchResult>();
int pageNum = (int)(ViewBag.PageNum ?? 1);
var pageResults =results.ToPagedList<CaseSearchResult>(pageNum, 5);

searchRequest.CaseSearchResults 上调用扩展方法会导致歧义错误;显式地将其强制转换为 results,然后在其上调用扩展方法就可以解决该问题。


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