Resolving extension methods/LINQ ambiguity

2020-01-27 02:56发布

问题:

I'm writing an add-in for ReSharper 4. For this, I needed to reference several of ReSharper's assemblies. One of the assemblies (JetBrains.Platform.ReSharper.Util.dll) contains a System.Linq namespace, with a subset of extension methods already provided by System.Core.

When I edit the code, it creates an ambiguity between those extensions, so that I cannot use OrderBy, for instance. How could I solve this? I would like to use the core LINQ extensions, and not the ones from ReSharper.

I get the following error when trying to compile:

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>)'

EDIT: I tried the suggestion below, unfortunately without luck. In the meanwhile, I "solved" the problem by removing references to System.Core. This way I could use the extensions provided by ReSharper DLL files.

I uploaded a sample program where I just imported the ReSharper DLL files I needed. I changed the alias of System.Core to SystemCore, added the extern alias directive, but it still didn't work. If I missed something, please let me know. P.S. The references are to ReSharper v4.1 DLL files installed in the default directroy in "C:\Program Files\JetBrains\ReSharper\v4.1\...".

回答1:

This is probably one of those rare cases where it makes sense to use an extern alias.

In the properties page for the reference to System.Core (i.e. under References, select System.Core, right-click and select "Properties"), change the "Aliases" value to "global,SystemCore" (or just "SystemCore" if it's blank to start with).

Then in your code, write:

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

That will make all the relevant types etc in System.Core.dll's System.Linq namespace available. The name "SystemCore" here is arbitrary - you could call it "DotNet" or something else if that would make it clearer for you.



回答2:

This isn't really an answer, but may provide an easier way for others to reproduce the issue (from the command-line - you could do it with two projects in Visual Studio if you want).

1) Create BadLinq.cs and build it as 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) Create 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) Compile Test.cs specifying the extern alias:

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

This fails with:

Test.cs(11,28): error CS1061: 'System.Array' does not contain a definition for 'Where' and no extension method 'Where' accepting a first argument of type 'System.Array' could be found (are you missing a using directive or an assembly reference?)

If you change it to not try to use an extension method (i.e. Enumerable.Where) it works fine with the extern alias.

I think this may be a compiler bug. I've emailed a private mailing list which the C# team reads - I'll update this answer or add a new one when I hear back.



回答3:

This is no longer an issue, since I am able to use the LINQ extensions, as provided by ReSharper DLL files, even while targeting .NET 3.0.

Mr. Skeet was right again! I am able to use full LINQ syntax, while targeting .NET 3.0 in the project's properties and not referencing System.Core!



回答4:

In order for ReSharper to be as compatible as possible with the variety of solutions it is used with, it is built against .NET 2.0. LINQ, etc. came in in C# 3.0, so they are not available in that version of the Framework. So, JetBrains added in their own version.

The solution is to build your addin against .NET 2.0 as well.



回答5:

I had the ambiguous reference problem using System.ComponentModel. Visual Studio was complaining that a DLL file exists in both v2 and v4. I was able to resolve it by removing the reference to the System DLL file and readding it.



回答6:

One solution would be to move all your code out to a partial class that uses the ReSharper code. In there, you'd import only the ReSharper namespace and not System.Core.

In the rest of the partial class, you'd import all the other namespaces you need, including System.Core, but not the ReSharper namespace.



回答7:

I had the same problem, even with extern alias, and I raised it as a compiler bug on Connect. The workaround for the time being is to forgo the extension method syntax.

The bug is fixed for Visual Studio 2010.



回答8:

It is really a compiler error.

I had the same problem, and I solved it just by cleaning and rebuilding the project. After that the problem disappeared.



回答9:

I had a similar situation. After two hours struggling I realized I had duplicate namespace names in my libraries. If you are using file Dynamic.cs published by Microsoft, the only thing you need to do is rename the current namespace to something else, and it will be fixed.

//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
{


回答10:

I found this same sort of ambiguity when use PagedList in MVC (.Net 4.5, MVC 5). I found that if I took the object for the argument that was ambiguous and cast it explicitly first, the problem was resolved. To if the ambiguity was between a method that takes System.Linq.Enumerable and one that takes System.Collections.Generic.IEnumerable as the parameter in question, and the source is of type System.Collections.Generic.IEnumerable, I don't use the extension method on it. I cast it.In this example, my repository method returns a 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);

Calling the extension method on searchRequest.CaseSearchResults caused the ambiguity error; explicitly casting to results and then calling the extension on that worked.