Extension methods conflict
Asked Answered
L

5

53

Lets say I have 2 extension methods to string, in 2 different namespaces:

namespace test1
{
    public static class MyExtensions
    {
        public static int TestMethod(this String str)
        {
            return 1;
        }
    } 
}

namespace test2
{
    public static class MyExtensions2
    {
        public static int TestMethod(this String str)
        {
            return 2;
        }
    } 
}

These methods are just for example, they don't really do anything.

Now lets consider this piece of code:

using System;
using test1;
using test2;

namespace blah {
    public static class Blah {
        public Blah() {
        string a = "test";
        int i = a.TestMethod(); //Which one is chosen ?
        }
    }
}

The Question:

I know that only one of the extension methods will be chosen.
Which one will it be ? and why ?

Edit:

This also bothers me, but not as much because it's a static method in a static class after all:

How can I choose a certain method from a certain namespace ?
Usually I'd use Namespace.ClassNAME.Method() ... But that just beats the whole idea of extension methods. And I don't think you can use Variable.Namespace.Method()

Loggia answered 12/3, 2011 at 15:55 Comment(6)
you can call test1.MyExtensions.TestMethod(a) if in doubt instead of a.TestMethod()Cephalonia
I don't know, but this I believe shouldn't really happen... give the method a more descriptive name in the class instead of trying to force the compiler to automagically choose one. I'm interested in seeing the responses.Bottrop
I don't say I'd ever do that. But lets say you're using some library, and unknowingly, there's an extension method over there with the same name.... just would like to know how it's chosenLoggia
Excellent question, I think - regardless of the alternate suggestions, this'll be good to know.Melyndamem
test1.MyExtensions.TestMethod(a) beats the idea of the extension methods. I've edited my question, that's not really what's bothering me.Loggia
Found this approach much cleaner, using static to specify the class for the extension you want to use - riptutorial.com/csharp/example/34/…Memento
T
34

I had this exact question so I found this post two years later. However, I think it is important to note that this will only not compile and give the "The call is ambiguous" error if the code calling the duplicate extension methods is not in the same namespace as one of them.

If the OP were to change the namespace of his class Blah to either test1 or test2, then the code compiles, and the extension in the same namespace as the caller is used - even when both namespaces are represented in the usings. So if Blah is in the test1 namespace, "1" is returned, and if Blah is in the test2 namespace, "2" is returned.

I think this is important to add to the above answers, because I think one mainstream use-case is to have extensions in a local class library that references external extension libraries (e.g. devs share a common utility library, but have some local custom extensions that might unwittingly have the same name). By maintaining the custom local extensions in the same namespace as the code that uses them, you can maintain the extension call syntax and not have to revert to treating them as static method calls.

Temperament answered 9/9, 2013 at 16:17 Comment(3)
Very important point. This actually solved a painful conflict I had been battling. Thanks!Cumulus
nb. namespaces are hierarchical, so if you want to override some third-party library's extension throughout your project, you can put your own extension in the top-level namespace that your project usesOmbudsman
Great comment. I was puzzling over the "This code will not compile" comments till I got to this one. My code definitely compiles, and this explains why the particular method that is chosen is chosen in the first place. For my specific case, it's perfect because I need to override the other extension method's logic.Puree
M
36

No method will be chosen: the call is ambiguous and will not compile.

Why can't you do Namespace.ClassNAME.Method()? Certainly there is nothing that prevents you from treating extension methods as normal static methods, and in fact this is the only way for you to fix the ambiguity and have the program compile.

Moran answered 12/3, 2011 at 16:1 Comment(3)
Ok, but is there a description somewhere about which one the compiler will choose and why ? And I was thinking about that I can't do Variable.namespace.Method()Loggia
the compiler will choose neither, your code will not compile.Munificent
@Yochai: the compiler will not choose because it will refuse to compile if both extension methods are visible. And as you say, variable.Namespace.Method() is not valid syntactically so it won't compile either.Moran
T
34

I had this exact question so I found this post two years later. However, I think it is important to note that this will only not compile and give the "The call is ambiguous" error if the code calling the duplicate extension methods is not in the same namespace as one of them.

If the OP were to change the namespace of his class Blah to either test1 or test2, then the code compiles, and the extension in the same namespace as the caller is used - even when both namespaces are represented in the usings. So if Blah is in the test1 namespace, "1" is returned, and if Blah is in the test2 namespace, "2" is returned.

I think this is important to add to the above answers, because I think one mainstream use-case is to have extensions in a local class library that references external extension libraries (e.g. devs share a common utility library, but have some local custom extensions that might unwittingly have the same name). By maintaining the custom local extensions in the same namespace as the code that uses them, you can maintain the extension call syntax and not have to revert to treating them as static method calls.

Temperament answered 9/9, 2013 at 16:17 Comment(3)
Very important point. This actually solved a painful conflict I had been battling. Thanks!Cumulus
nb. namespaces are hierarchical, so if you want to override some third-party library's extension throughout your project, you can put your own extension in the top-level namespace that your project usesOmbudsman
Great comment. I was puzzling over the "This code will not compile" comments till I got to this one. My code definitely compiles, and this explains why the particular method that is chosen is chosen in the first place. For my specific case, it's perfect because I need to override the other extension method's logic.Puree
L
13

As Jon says, if both of these exist when you do the compilation, the compilation will just fail.

But if only one exists at the time of compilation and a external library later gets updated to add the second, the code you compiled will still continue to use the first one. This is because the compiler interally turns your code into the longhand form of calling namespace.classname.method.

Lema answered 12/3, 2011 at 16:18 Comment(0)
A
4

I migrated big solution from .Net 4.7.1 to .Net 4.7.2. We use LINQ in our code, and we use well known and established library with name MoreLinq https://www.nuget.org/packages/morelinq/.

.Net 4.7.1 does not have .ToHashSet() methods. We used .ToHashSet() from MoreLinq library. And in the same class in the same cs-file we have both using System.Linq; and using MoreLinq;.

I retargeted a project to .Net 4.7.2 and the compiler showed The call is ambiguous error as described above. The reason was that .Net 4.7.2 added new extension methods with the same name .ToHashSet().

I cannot reimplement huge code base. I cannot replace MoreLinq with another library. This is what I did. I created a new class in a new file where I have using System.Linq; but not using MoreLinq;. This is the file (ToHashsetHelpers.cs):

using System.Collections.Generic;
using System.Linq;

namespace Common.Helpers
{
    /// <summary>
    /// This class with only one method helps to resolve
    /// name conflict between .Net 4.7.2 and MoreLinq libraries.
    ///
    /// .Net 4.7.2 introduced a new extension method named '.ToHashSet()'.
    /// But MoreLinq already has the same method.
    ///
    /// After migrating our solution from .Net 4.7.1 to 4.7.2
    /// C# compiler shows "The call is ambiguous" error.
    ///
    /// We cannot have both "using System.Linq;" and "using MoreLinq;" in the same C# file that
    /// uses '.ToHashSet()'.
    ///
    /// The solution is to have method with different name in a file like this.
    /// </summary>
    public static class ToHashsetHelpers
    {
        /// <summary>
        /// The name of this method is ToHashset (not ToHashSet)
        /// </summary>
        public static HashSet<TSource> ToHashset<TSource>(this IEnumerable<TSource> source)
        {
            // Calling System.Linq.Enumerable.ToHashSet()
            return source.ToHashSet();
        }
    }
}

And I renamed all .ToHashSet() to .ToHashset() in entire solution.

Ashlaring answered 1/11, 2018 at 12:0 Comment(0)
R
0

I was wondering about the same question and I did a quick test inside an asp.net core 6 project.

If you try this, it does not compile. Pretty much similar as other ambiguous calls or statements not involving extension methods.

using TestExtNs;
using TestExtNs2;

namespace YourBlazorProject.Server
{
    public class TestMe
    {
        public void Test() { }
    }
}

namespace TestNs
{
    public static class Tester
    {
        public static void RunTest() // Exec this
        {
            var x = new YourBlazorProject.Server.TestMe();
            x.Test();
            x.TestExt(); // does not compile !!! error CS0121

            TestExtNs.TesterExt.TestExt(x); //explicit call as working alternative
        }
    }
}

namespace TestExtNs
{
    public static class TesterExt
    {
        public static void TestExt(this YourBlazorProject.Server.TestMe y)
        {
            Console.WriteLine("ExtNs");
            System.Diagnostics.Debug.WriteLine("#> DIAG: ExtNs");
        }
    }
}

namespace TestExtNs2
{
    public static class TesterExt
    {
        public static void TestExt(this YourBlazorProject.Server.TestMe y)
        {
            Console.WriteLine("ExtNs2");
            System.Diagnostics.Debug.WriteLine("#> DIAG: ExtNs2");
        }
    }
}

Alternative: If there is an extension method in the same namespace, this 'closer one' is used; otherwise it won't compile.

// SomeTest.cs (example for 'closer namespace')

using TestExtNs; // This is hard REQUIREMENT for bringing the extension method from TestExtNs into scope !!!

namespace YourBlazorProject.Server
{
    public class TestMe
    {
        public void Test() { }
    }
}

namespace TestNs
{
    public static class Tester
    {
        public static void RunTest() // Exec this
        {
            var x = new YourBlazorProject.Server.TestMe();
            x.Test();

            x.TestExt(); //Ns
            TestExt(x); //Ns
            TestExtNs.TesterExt.TestExt(x); //ExtNs
        }

        public static void TestExt(this YourBlazorProject.Server.TestMe y)
        {
            Console.WriteLine("Ns"); //writes to the Console Window of the *.Server.exe if its out-of-process hosted. if hosted on IISExp then its visbible in if IISExp is launched from console according to stackoverflow.
            System.Diagnostics.Debug.WriteLine("#> DIAG: Ns"); //writes to the VS output console
        }
    }
}

namespace TestExtNs
{
    public static class TesterExt
    {
        public static void TestExt(this YourBlazorProject.Server.TestMe y)
        {
            Console.WriteLine("ExtNs");
            System.Diagnostics.Debug.WriteLine("#> DIAG: ExtNs");
        }
    }
}

Output:
Ns
Ns
ExtNs
Ruy answered 1/4, 2022 at 14:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.