web-dev-qa-db-ja.com

拡張メソッドの競合

2つの異なる名前空間に2つの文字列拡張メソッドがあるとしましょう。

_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;
        }
    } 
}
_

これらのメソッドはほんの一例であり、実際には何もしません。

次に、このコードについて考えてみましょう。

_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 ?
        }
    }
}
_

質問

拡張メソッドの1つだけが選択されることを私は知っています。
どちらになりますか?なぜ ?

編集:

これも気になりますが、結局のところ静的クラスの静的メソッドであるため、それほどではありません。

特定の名前空間から特定のメソッドを選択するにはどうすればよいですか?
通常はNamespace.ClassNAME.Method()を使用します...しかし、それは拡張メソッドの全体的な考え方を上回っています。そして、私はあなたがVariable.Namespace.Method()を使うことができないと思います

39
Yochai Timmer

私はこの正確な質問をしたので、2年後にこの投稿を見つけました。ただし、これはコンパイルされず、「呼び出しがあいまいです」エラーが発生するだけではないことに注意することが重要だと思いますコードの場合重複する拡張メソッドの呼び出しは、それらの1つと同じ名前空間ではありません

OPがクラスの名前空間Blahtest1またはtest2に変更した場合、コードはコンパイルされ、呼び出し元と同じ名前空間の拡張子が使用されます-両方の名前空間usingsで表されている場合でも。したがって、Blahtest1名前空間にある場合、「1」が返され、Blahtest2名前空間にあり、「2」が返されます。

これは上記の回答に追加することが重要だと思います。主流のユースケースの1つは、外部拡張ライブラリを参照するローカルクラスライブラリに拡張を含めることだと思います(たとえば、開発者は共通のユーティリティライブラリを共有しますが、いくつかのローカルカスタム拡張を持っています無意識のうちに同じ名前になる可能性があります)。カスタムローカル拡張機能を、それらを使用するコードと同じ名前空間で維持することにより、拡張機能呼び出し構文を維持でき、静的メソッド呼び出しとして扱うことに戻る必要はありません。

26
mdisibio

メソッドは選択されません。呼び出しはあいまいであり、コンパイルされません。

なぜNamespace.ClassNAME.Method()ができないのですか?確かに、拡張メソッドを通常の静的メソッドとして扱うことを妨げるものは何もありません。実際、これがあいまいさを修正してプログラムをコンパイルする唯一の方法です。

29
Jon

Jonが言うように、コンパイルを行うときにこれらの両方が存在する場合、コンパイルは失敗します。

ただし、コンパイル時に1つしか存在せず、後で外部ライブラリが更新されて2つ目が追加された場合でも、コンパイルしたコードは引き続き最初のライブラリを使用します。これは、コンパイラが内部的にコードをnamespace.classname.methodを呼び出す長い形式に変換するためです。

12
Robert Levy

大きなソリューションを.Net4.7.1から.Net4.7.2に移行しました。コードではLINQを使用し、MoreLinqという名前の有名で確立されたライブラリを使用しています https://www.nuget.org/packages/morelinq/

.Net4.7.1には.ToHashSet()メソッドがありません。 MoreLinqライブラリの.ToHashSet()を使用しました。また、同じcsファイルの同じクラスには、_using System.Linq;_と_using MoreLinq;_の両方があります。

プロジェクトを.Net4.7.2に再ターゲットしたところ、コンパイラーは上記のように_The call is ambiguous_エラーを示しました。その理由は、.Net4.7.2が同じ名前.ToHashSet()の新しい拡張メソッドを追加したためです。

巨大なコードベースを再実装することはできません。 MoreLinqを別のライブラリに置き換えることはできません。これが私がしたことです。 _using System.Linq;_はあるが、_using MoreLinq;_がない新しいファイルに新しいクラスを作成しました。これはファイル(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();
        }
    }
}
_

そして、ソリューション全体ですべての.ToHashSet()の名前を.ToHashset()に変更しました。

3
Sergei Zinovyev