31

以下のコードでは、親バージョンのmethodTwoにアクセスする方法を2つ試しましたが、結果は常に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();
    }
}

更新ChrisWの投稿:

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

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


8 답변


35

ILレベルでは、おそらくあなたはcallではなくcallvirtそして、仕事を成し遂げる - しかし私達が自分達をC#に限定すれば;-p(編集するくそー!ランタイムはあなたを止めます:VerificationExceptionmsgstr "操作によってランタイムが不安定になる可能性があります。"を削除virtualそしてそれはうまくいきます。半分にしても巧妙すぎる...)

の中ChildClassタイプ、あなたは使用することができますbase.methodTwo()しかし、これは外部的には不可能です。また、あなたは1つ以上のレベルを下ることはできません - ありませんbase.base.Foo()サポート。

ただし、メソッド隠蔽を使用して多態性を無効にすると、次のようになります。回答あなたは欲しいが、悪い理由で:

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

これで、変数が変数として定義されているかどうかによって、同じオブジェクトから異なる答えを得ることができます。ChildClassまたはParentClass


  • なぜ「new」を使用しているのですか。この場合は悪い? 「new」を使用することはありますか。いいね ? - Peter Huber
  • @PeterHuberは多相を破るためです。の主なユースケースnewこのシナリオでは、型をより特殊化するためのものです。例えば、DbCommandがありますpublic DbParameterCollection Parameters {get;} - しかしSqlCommand : DbCommandがありますpublic new SqlParameterCollection Parameters {get;}。両方のAPIが同じを返す実例(別によって達成されるprotected abstract財産、すなわちDbParameterCollection - 値は常にあるあるSqlParameterCollection) - しかしより具体的な型はより具体的な方法でそれを公開します - Marc Gravell
  • @MarcGravell:情報をありがとう、私は実際には詳細に理解していませんが。たとえば、プロパティが仮想的で上書き可能かどうかはわかりません。方法は理解しやすいです。私の基本クラスは、オーバーライドされたものではなく、ベース内の別の仮想メソッドを呼び出す必要があるため、この問題に遭遇しました。基本クラスがオーバーライドされたバージョンを呼び出し、それが再び基本クラスを呼び出したため、スタックオーバーフローが発生しました。上書きを新しいものに変更することで、今ではすべてうまくいきます。基本仮想メソッドを呼び出す必要があることを指定する方法があると思いますが、不可能なようです。 - Peter Huber

51

の中にChildClass.methodTwo()電話できますbase.methodTwo()

クラス外、電話((ParentClass)a).methodTwo()) 意志コールChildClass.methodTwo。これが仮想メソッドが存在する全体的な理由です。


  • ..そして、-1は、それが使いすぎになったり誤用されたりするすべての方法に対してです。 - Michael Meadows
  • Michael - 多相行動について誰かの説明を軽蔑しましたか。クレイグは単に物事がなぜそのように機能するのかを説明していたのです。なぜなら、彼らがあなたがたぶん濫用されていると信じるという概念を説明するからといって良い答えを軽視することについては怠けないでください。 - Andrew Hare
  • 私は彼が冗談を言っていたと思います。 :) - Craig Stuntz
  • 投票は行われませんでした。多態性の乱用について私の-1セントを投入したいだけでした。それが明確でない場合は申し訳ありません。 - Michael Meadows

3

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

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

Kenneth Xuありがとうございます。



2

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

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());

私の場合、子クラスがPeerの概念を導入したので、それをオーバーライドする必要がありました。methodTwo()ピアの基本バージョンを起動します。それをオーバーライドすることによって、それはそれを隠した...それともそれをしましたか?この技術は助けになりました。

class ChildClass : ParentClass {
    ChildClass peer;
    override public int methodTwo() {
        return peer.ParentClass_methodTwo();
    }
    private int ParentClass_methodTwo() {
        return base.methodTwo();
    }
}


2

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

    }
}

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

    }
}

上記のコードは親メソッドを上書きしますが、親メソッドの値は変更されません。

Bothから値を返すことができます親クラスそして子どもクラス下記のコードを使用して -

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

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


  • あなたは参考文献を持っていますか? - gdmanandamohon

1

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


  • これは悪い答えではありません - 私は彼があなたが子供のインスタンスからオーバーライドされた親メソッドを呼び出すことができないことを意味したと思います、そしてそれは質問の精神でした。 - Andrew Hare

0

ParentClassのインスタンスを直接作らない限り不可能だと思います。 継承、ポリモーフィズムのまさしくその本質は…


0

私は助けを探してこれにつまずいたし、メソッドの先祖バージョンを呼び出すことへの私自身のアプローチで終わりました。これは、先祖クラスを編集できると仮定した場合の回避策です。

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

リンクされた質問


関連する質問

最近の質問