web-dev-qa-db-ja.com

オーバーライドされたメソッドの親バージョンを呼び出す方法はありますか? (C#.NET)

以下のコードでは、2つの方法でmethodTwoの親バージョンにアクセスしようとしましたが、結果は常に2でした。これら2つのクラスを変更せずに、ChildClassインスタンスから1の結果を取得する方法はありますか?

class ParentClass
{
    public int methodOne()
    {
        return methodTwo();
    }

    virtual public int methodTwo()
    {
        return 1;
    }
}

class ChildClass : ParentClass
{
    override public int methodTwo()
    {
        return 2;
    }
}

class Program
{
    static void Main(string[] args)
    {
        var a = new ChildClass();
        Console.WriteLine("a.methodOne(): " + a.methodOne());
        Console.WriteLine("a.methodTwo(): " + a.methodTwo());
        Console.WriteLine("((ParentClass)a).methodTwo(): "
         + ((ParentClass)a).methodTwo());
        Console.ReadLine();
    }
}

pdate ChrisWが投稿しました:

クラスの外からは、簡単な方法はわかりません。しかし、おそらく、リフレクションを試した場合はどうなるかわかりません。Type.GetMethodメソッドを使用して、ParentClassのメソッドに関連付けられているMethodInfoを見つけ、MethodInfo.Invokeを呼び出します。

その答えは削除されました。好奇心のために、そのハックがうまくいくかどうか疑問に思っています。

39
Jader Dias

ILレベルでは、おそらくcallではなくcallvirtを発行して、ジョブを完了することができますが、C#に限定すると、-p(edit = darn!ランタイムが停止します:VerificationException: "操作によってランタイムが不安定になる可能性があります。"; virtualを削除すると、正常に機能します。

ChildClassタイプの内部では、base.methodTwo()を使用できますが、これは外部では不可能です。また、複数のレベルを下げることもできません-base.base.Foo()サポートはありません。

ただし、メソッド非表示を使用してポリモーフィズムを無効にすると、必要なanswerを取得できますが、理由は次のとおりです。

class ChildClass : ParentClass
{
    new public int methodTwo() // bad, do not do
    {
        return 2;
    }
}

これで、変数がChildClassとして定義されているか、ParentClassとして定義されているかに応じて、同じオブジェクトから異なる回答を取得できます。

37
Marc Gravell

ChildClass.methodTwo()内では、base.methodTwo()を呼び出すことができます。

クラスの外で((ParentClass)a).methodTwo()を呼び出すwill call ChildClass.methodTwo。これが、仮想メソッドが存在する理由のすべてです。

62
Craig Stuntz

上記のように、PRODUCTIONコードで「base.base」を呼び出す必要がある場合、クラス設計に問題があります。ただし、コンパイルできない外部ライブラリを使用しているときに、いくつかの回避策をデバッグまたは検索している場合は、この手法を使用することはかなり正当です。 C#がこのオプションを直接提供しないのは不愉快です。それでも、Kenneth XuソリューションをILジェネレーターとEmitで使用できます。できます。

class A { public virtual string foo() { return "A"; } }

class B : A { public override string foo() { return "B"; } }

// now in class C
class C : B {}      
// we can call virtual method "foo" from A using following code
MethodInfo fooA = typeof(A).GetMethod("foo", BindingFlags.Public | BindingFlags.Instance);

DynamicMethod baseBaseFoo = new DynamicMethod(
            "foo_A",
            typeof(string),
            new[] { typeof(A) },
            typeof(A));
        ILGenerator il = baseBaseFoo.GetILGenerator();
        il.Emit(OpCodes.Ldarg, 0);
        il.EmitCall(OpCodes.Call, fooA, null);
        il.Emit(OpCodes.Ret);

// call foo() from class A, it returns "A"
(string)baseBaseFoo.Invoke(null, new object[] { this });

参照と完全なサンプルについては、 http://kennethxu.blogspot.cz/2009/05/cnet-calling-grandparent-virtual-method.html を参照してください

ケネス・シュー、ありがとう!

5
Michal
public class Parent 
{
    public string Method()
    {
       return "parent";

    }
}

public class Child:Parent
{
    public string Method()
    {
       return "child";

    }
}

上記のコードは、親メソッドのオーバーライドに成功しましたが、親メソッドの値はまだ変更されていません。

以下のコードを使用して、両方の親から値を返すことができます親クラスおよび子クラス-

Child child = new Child();
string result = (((Parent)child).Method()) + child.Method();

しかし、Visual Studioはコンパイル時に警告を表示します。

2
HammrerEngineer

Mark Gravellが言ったように、いや、外部ではありません。しかし、ここで私が使用した別のハックがあります。 ChildClassは、ParentClassからのmethodTwo()をそれ自体または外部で使用するために公開できます。あなたの場合:

_class ChildClass : ParentClass {
    override public int methodTwo() {
        return 2;
    }
    public int ParentClass_methodTwo() {
        return base.methodTwo();
    }
}

// Now instead of
Console.WriteLine("ParentClass methodTwo: " + ((ParentClass)a).methodTwo());
// use
Console.WriteLine("ParentClass methodTwo: " + a.ParentClass_methodTwo());
_

私の場合、子クラスにはピアの概念が導入されており、ピアで基本バージョンを呼び出すにはmethodTwo()をオーバーライドする必要がありました。しかし、それを上書きすることで、それはそれを隠しました...それともそれをしましたか?このテクニックが助けになりました。

_class ChildClass : ParentClass {
    ChildClass peer;
    override public int methodTwo() {
        return peer.ParentClass_methodTwo();
    }
    private int ParentClass_methodTwo() {
        return base.methodTwo();
    }
}
_
2
Keith Robertson

私の知る限り、メソッドがオーバーライドされると、親メソッドを呼び出すことができなくなります。

1
Vince Panuccio

私はこれに助けを求めて偶然見つけ、メソッドの祖先バージョンを呼び出すための自分自身のアプローチに終わりました。これは、祖先クラスを編集できることを前提とした回避策の詳細です。

必要なパラメーターを使用して、祖先クラスの問題の機能を静的メソッドに配置します。そのクラスのメソッドはそれを呼び出すことができるので、機能を複製する必要はなく、子クラスは必要に応じて静的メソッドを介してその機能を呼び出すことができます。このようにして、メソッド内でも実行でき、呼び出したい特定のクラスを引用できます。また、単なる「親」よりも後の祖先にアクセスできます。これは、「ベース」の使用の制限です。

0
user3208955

あなたが直接ParentClassのインスタンスを作成しない限り、それは不可能だと思います。それこそが継承の本質、ポリモーフィズムです...

0
Julius A