web-dev-qa-db-ja.com

クラスメソッドを拡張メソッドでオーバーライドする方法はありますか?

クラス内のメソッドを拡張メソッドでオーバーライドしたい場合がありました。 C#でそれを行う方法はありますか?

例えば:

public static class StringExtension
{
    public static int GetHashCode(this string inStr)
    {
        return MyHash(inStr);
    }
}

私がこれをしたかったケースは、文字列のハッシュをデータベースに保存し、文字列クラスのハッシュを使用するすべてのクラス(つまり、辞書など)で同じ値を使用できるようにすることです。組み込みの.Netハッシュアルゴリズムは、フレームワークの1つのバージョンから次のバージョンへの互換性が保証されていません。これを独自のものに置き換えたいと思います。

文字列クラスまたはGetHashCodeメソッドだけに限定されないように、クラスメソッドを拡張メソッドでオーバーライドしたい場合もあります。

既存のクラスをサブクラス化することでこれを実行できることは知っていますが、多くの場合、拡張機能を使用して実行できると便利です。

95
Phred Menyhert

番号;拡張メソッドは、適切なシグネチャを持つインスタンスメソッドよりも優先されることはなく、ポリモーフィズムに関与することもありません(GetHashCodevirtualメソッドです)。

89
Marc Gravell

メソッドに別のシグネチャがある場合、それを実行できます-そのため、あなたの場合:いいえ。

しかし、そうでない場合は、探していることを行うために継承を使用する必要があります。

7
Chris Brandsma

拡張メソッドはインスタンスではないため、答えはノーです。クラスのインスタンスを使用して静的メソッドを呼び出すことができるインテリセンス機能に似ています。あなたの問題の解決策は、特定のメソッド(例えば、GetHashCode())の実行をインターセプトし、他の何かを行うインターセプターになると思います。オブジェクトファクトリ(またはCastleのIoCコンテナ)により、実行時に生成された動的プロキシを介してインターフェイスをインターセプトできます(Caslteでは、クラスの仮想メンバーもインターセプトできます)

2
Beatles1692

クラスメソッドと同じシグネチャで拡張メソッドを呼び出す方法を見つけましたが、あまりエレガントではないようです。拡張メソッドで遊んでいると、文書化されていない動作に気づきました。サンプルコード:

public static class TestableExtensions
{
    public static string GetDesc(this ITestable ele)
    {
        return "Extension GetDesc";
    }

    public static void ValDesc(this ITestable ele, string choice)
    {
        if (choice == "ext def")
        {
            Console.WriteLine($"Base.Ext.Ext.GetDesc: {ele.GetDesc()}");
        }
        else if (choice == "ext base" && ele is BaseTest b)
        {
            Console.WriteLine($"Base.Ext.Base.GetDesc: {b.BaseFunc()}");
        }
    }

    public static string ExtFunc(this ITestable ele)
    {
        return ele.GetDesc();
    }

    public static void ExtAction(this ITestable ele, string choice)
    {
        ele.ValDesc(choice);
    }
}

public interface ITestable
{

}

public class BaseTest : ITestable
{
    public string GetDesc()
    {
        return "Base GetDesc";
    }

    public void ValDesc(string choice)
    {
        if (choice == "")
        {
            Console.WriteLine($"Base.GetDesc: {GetDesc()}");
        }
        else if (choice == "ext")
        {
            Console.WriteLine($"Base.Ext.GetDesc: {this.ExtFunc()}");
        }
        else
        {
            this.ExtAction(choice);
        }
    }

    public string BaseFunc()
    {
        return GetDesc();
    }
}

私が気づいたのは、拡張メソッド内から2番目のメソッドを呼び出した場合、署名にも一致するクラスメソッドがあったとしても、署名に一致した拡張メソッドを呼び出すことです。たとえば、上記のコードで、Ext.Func()を呼び出し、次にele.GetDesc()を呼び出すと、予想される文字列 "Base GetDesc"の代わりに戻り文字列 "Extension GetDesc"を取得します。

コードのテスト:

var bt = new BaseTest();
bt.ValDesc("");
//Output is Base.GetDesc: Base GetDesc
bt.ValDesc("ext");
//Output is Base.Ext.GetDesc: Extension GetDesc
bt.ValDesc("ext def");
//Output is Base.Ext.Ext.GetDesc: Extension GetDesc
bt.ValDesc("ext base");
//Output is Base.Ext.Base.GetDesc: Base GetDesc

これにより、クラスメソッドと拡張メソッドの間を自由に行き来できますが、重複する「パススルー」メソッドを追加して、目的の「スコープ」に到達する必要があります。ここでは、より良いWordがないことをスコープと呼びます。誰かが私にそれが実際に何と呼ばれているのか教えてくれることを願っています。

私の「パススルー」メソッド名から、1つまたは2つのメソッドが同じシグネチャを持つ複数のメソッドのパススルーとして機能できることを期待して、デリゲートを渡すというアイデアを思いついたのではないかと思います。残念ながら、デリゲートをアンパックすると、別の拡張メソッドの内部からであっても、常に拡張メソッドよりもクラスメソッドを選択するようになりませんでした。 「スコープ」は問題ではなくなりました。私はActionとFuncデリゲートをあまり使用していませんが、経験豊富な人がその部分を理解できるかもしれません。

0
DanK