5

次のコードでは、オーバーロードされたメソッドがあります。1つはClazzA型のパラメーターを取り、もう1つはClazzB型のパラメーターを取ります。示されているコードでは、最初のGetDescriptionメソッド(ClazzAをパラメーターとして受け取るメソッド)が呼び出されます。私はその理由を理解していると思います。
私の質問は、基になるオブジェクトがクラスB型の場合(各オブジェクトを検査してclazzBにキャストする必要なしに)、clazzBを最初に呼び出すメソッドを使用するエレガントな方法はありますか?

public class ClazzA
{
    public virtual string Descr { get { return "A"; } }
}

public class ClazzB : ClazzA
{
    public override string Descr { get { return "B"; } }
}

public static class test
{
    public static void Main()
    {
        ClazzA test = new ClazzB();
        GetDecription(test);
    }

    public static void GetDecription(ClazzA someClazz)
    {
        Debug.WriteLine("I am here");
    }

    public static void GetDecription(ClazzB someClazz)
    {
        Debug.WriteLine("I want to be here");
    }
}

出力:「私はここにいます」

'test'はClassB型なので、2番目のメソッドを呼び出したいと思います。現在、私が持っている唯一の2つの解決策は次のとおりです。

  1. if(test is ClazzB)return GetDescription((ClazzB)test);

また

  1. ClassAでもほぼ同じことを行います...タイプを確認し、2番目のメソッドに委任します

これらは両方とも、オブジェクトのタイプを判別するためにオブジェクトを検査する必要があります

4

8 に答える 8

9

過負荷はコンパイル時に決定されます。参照のコンパイル時のタイプは、ClazzAオーバーロードが選択されるようになっています。あなたが求めているのは、多重ディスパッチに関連しています。C#およびC ++やJavaなどの他の多くの言語は、(virtualメソッドを介した)単一のディスパッチのみをサポートします。これを回避するために人々が思いついた方法はたくさんあります。これを行う最も純粋なOOの方法は、ビジターパターンです。クラスを変更してメソッド( )を含め、訪問者()のメソッドへの参照をAccept渡します。これは、各サブクラスのメソッドをオーバーライドして、オブジェクトの実際の型になるために機能します。訪問者に必要なのは、サポートするサブクラスごとの特定のメソッドだけです(詳細については、ウィキペディアを参照してください)。thisVisitAcceptthis

サンプル:

public class ClazzA
{
   public virtual string Accept(ClassVisitor visitor)
   {
      return visitor.Visit(this);
   }
}

public class ClazzB : ClazzA
{
   public override string Accept(ClassVisitor visitor)
   {
      return visitor.Visit(this);
   }
}

public abstract class ClassVisitor
{
  public abstract string Visit(ClazzA a);
  public abstract string Visit(ClazzB b);
}

public class GetDescriptionVisitor : ClassVisitor
{
    public override string Visit(ClazzA a)
    {
       return "A";
    }

    public override string Visit(ClazzB b)
    {
       return "B";
    }
}

使用法:

ClassVisitor visitor = new GetDescriptionVisitor();
ClazzA b = new ClazzB();
Console.WriteLine(b.Accept(visitor)); // prints "B"
于 2012-10-27T01:50:50.847 に答える
3

メソッドのオーバーロード解決はコンパイル時に発生するためです。この状況に対処するという点で、C#4を使用している場合は、動的を使用して、過負荷の解決が実行時に延期されるようにすることができます。

dynamic instance = new ClazzB();
Console.WriteLine(GetDescription(instance));

または、次のようなビジターパターンを使用することもできますが、このダブルディスパッチアプローチは大変な作業のように感じます。すべての派生型で再実装する必要がある反復Visitメソッドに注意してください!

public interface IVisitable
{
    string Visit(DescriptionVisitor visitor);
}

public class ClazzA : IVisitable
{
    public virtual string Visit(DescriptionVisitor visitor)
    {
        return visitor.Visit(this);
    }
}

public class ClazzB : ClazzA
{
    public override string Visit(DescriptionVisitor visitor)
    {
        return visitor.Visit(this);
    }
}

public class DescriptionVisitor
{
    public string Visit(ClazzA item) { return "Description A"; }
    public string Visit(ClazzB item) { return "Description B"; }
}

その後、以下は最終的に、ClazzBを取得するDescriptionVisitorのオーバーロードを呼び出します。

var visitor = new DescriptionVisitor();
ClazzA a = new ClazzB();
Console.WriteLine(a.Visit(visitor));
于 2012-10-27T02:17:56.553 に答える
2

あなたがやろうとしていることは、おそらく次のように、ポリモーフィズムを使用して実行する方が良いでしょう。

public interface IProvideDescription { 
  string GetDescription();
}

public class A : IProvideDescription {
  public string GetDescription() {
    return "I'm an A";
  }
}

public class B : IProvideDescription {
  public string GetDescription() {
    return "I'm a B";
  }
}

// to execute:

IProvideDescription x = new A();
Console.WriteLine(x.GetDescription());
x = new B();
Console.WriteLine(x.GetDescription());
于 2012-10-26T23:11:00.750 に答える
1

タイトルの質問に答えるには(「...基本クラスが優先されるのはなぜですか?」)、変数testが何として宣言されているかを確認します(答え:基本クラス)。オーバーロードが選択されている場合、メソッド呼び出しは、型の変数を渡していることだけを認識しますClazzA。確かに、あなたはそれにタイプのオブジェクトを割り当てましたClazzBが、あなたの割り当てステートメントがもっと複​​雑だったとしましょう:

ClazzA test = GiveMeSomeObject();

メソッドの選択は、型の安全性を提供するためにコンパイル時に行う必要があります。

于 2012-10-26T23:18:43.077 に答える
1

.Net 4.0で導入された「dynamic」キーワードを使用して、必要な動作を得ることができます。実行時にタイプを評価し、適切なオーバーロードを選択します。

public static class test
{
    public static void Main()
    {
        dynamic test = new ClazzB();
        GetDecription(test);
    }

    public static void GetDecription(ClazzA someClazz)
    {
        Debug.WriteLine("I am here");
    }

    public static void GetDecription(ClazzB someClazz)
    {
        Debug.WriteLine("I want to be here");
    }
}
于 2012-10-27T00:06:36.813 に答える
1

通常、クラスタイプを識別することは外部クラスの責任ではありません。ポリモーフィックな動作が必要な場合は、GetDescriptionを仮想関数としてClassAに入れてから、ClassBでオーバーライドします。これは概念的には正しいことです。

于 2012-10-27T07:29:58.623 に答える
0

@rokenが述べたように、プロパティがオーバーライドされるBため、実際には例になります。Descrこれがすべての場合は、ClazzBオーバーロードを削除し、すでに持っているポリモーフィックな動作を使用します。dynamic実際にメソッドで別のことを行う必要があり、オーバーロードがそれを行うための最良の方法である場合は、オーバーロード解決を介してそれを行うことができます。

GetDecription((dynamic)test);

GetDescription(test)ただし、これには、パフォーマンスや、意味のあるコンパイル時テストの欠如など、いくつかの欠点があります。内でランタイムチェックを行うことをお勧めしますGetDecription(ClazzA)

if (someClazz is ClazzB)
{
    GetDescription((ClazzB)someClazz);
    return;
}
于 2012-10-26T23:08:28.767 に答える
-2

あなたはたった一つのGetDescription()方法でやっていくことができます:

public String GetDescription(ClassA in) {
    if (in is ClassB) {
        return (in as ClassB).Descr 
    }

    return in.Descr;
}
于 2012-10-26T23:05:47.003 に答える