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


98

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

例えば:

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

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

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

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


ディクショナリはメモリ内データ構造であるため、ハッシュアルゴリズムがフレームワークのバージョン間で変化するかどうかによって、どのような違いがありますか?フレームワークのバージョンが変更された場合、明らかにアプリケーションが再起動され、ディクショナリが再構築されています。
デビッドネルソン

回答:


91

番号; 拡張方法は、多型に関与する(決して適切な署名でインスタンスメソッドよりも優先されず、決してGetHashCodeあるvirtual方法)。


「...ポリモーフィズムに関与しない(GetHashCodeは仮想メソッドです)」拡張メソッドが仮想であるために、ポリモーフィズムに関与しない理由を別の方法で説明できますか?これら2つのステートメントとの関係を確認するのに問題があります。
アレックス

3
@Alexは、仮想について言及することで、ポリモーフィックであるとはどういう意味かを明確にしています。GetHashCodeの実質的にすべての使用において、具象型は不明です-したがって、ポリモーフィズムが機能しています。そのため、拡張メソッドは、通常のコンパイラーで優先されたとしても役に立ちません。OPが本当に必要としているのは、モンキーパッチです。どのc#と.netが促進しないか。
マークグラベル

4
それは悲しいことです。C#では、たとえば.ToString()配列のように、多くの誤解を招くような意味のないメソッドが使用されます。それは間違いなく必要な機能です。
Hi-Angel

では、コンパイラエラーが発生しないのはなぜですか。たとえば、タイプします。namespace System { public static class guilty { public static string ToLower(this string s) { return s.ToUpper() + " I'm Malicious"; } } }
低レベル

@LowLevelなぜあなたは?
マークグラベル

7

メソッドに別の署名がある場合は、それを行うことができます-したがって、あなたの場合:いいえ。

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


2

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


0

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

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番目のメソッドを呼び出すと、シグニチャにも一致するクラスメソッドがあったとしても、シグニチャに一致する拡張メソッドが呼び出されるということです。たとえば上記のコードで、ExtFunc()を呼び出し、次に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

これにより、クラスメソッドと拡張メソッドの間を自由に行き来することができますが、希望する「スコープ」に到達するには、重複する「パススルー」メソッドを追加する必要があります。ここでは、より良い言葉がないため、これをスコープと呼びます。うまくいけば、誰かが実際にそれが何と呼ばれるかを私に知らせることができます。

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.