10

演算子 +、-、* などをオーバーロードしたい抽象クラス Vector があり
ます。派生クラスでこれらを使用して、呼び出し元のオブジェクトと同じ型のオブジェクトを取得できるようにしたいと考えています。
ジェネリックを試してみましたが(簡単に言えば次のように)、合法的な方法が見つかりませんでした:

public static T operator +<T>( T V1, T V2) where T : Vector
{
     //some calculation
     return new T(args);
}

次に、基本クラスを使用してそれを実行しようとしました:

    public static Vector operator+(Vector V1, Vector V2)
    {
        if (V1.Dimension != V2.Dimension)
            throw new VectorTypeException("Vector Dimensions Must Be Equal");
        double[] ArgList = new double[V1.Dimension];
        for (int i = 0; i < V1.Dimension; i++) { ArgList[i] = V1[i] + V2[i]; }

        return (Vector)Activator.CreateInstance(V1.GetType(), new object[] { ArgList});
    }

このメソッドが 2 つの子オブジェクトで渡された場合、それらに対して操作を実行し、同じ遺産の新しいオブジェクトを返す必要があります。

これで私が遭遇した問題は、そのようなすべての子クラスに適切な署名を持つコンストラクターが必要であり、オブジェクトを作成するために基本コンストラクターを呼び出すことができないことです。

(a)これらのいずれかを機能させる方法、または(b)これを別の方法でエレガントに行う方法は何ですか?

4

3 に答える 3

13

サブクラスがオーバーライドできるインスタンスレベルの抽象メソッドを宣言できます。

public abstract class Vector
{
    protected abstract Vector Add(Vector otherVector);

    public static Vector operator +(Vector v1, Vector v2)
    {
        return v1.Add(v2);
    }
}

public class SubVector : Vector
{
    protected override Vector Add(Vector otherVector)
    {
        //do some SubVector addition
    }
}

特に複数のサブクラスでいくつかの問題が発生する可能性があります (SubVector追加する方法を知る必要がありますか? クラスSomeOtherSubVectorClassを追加するとどうなりますThirdVectorTypeか?) およびおそらく null ケースの処理。また、可換演算に関してはSubVector.Add、 が同じように動作することを確認してください。SomeOtherSubVectorClass.Add

編集:他のコメントに基づいて、次のようなことができます:

public class Vector2D : Vector
{
    public double X { get; set; }
    public double Y { get; set; }

    protected override Vector Add(Vector otherVector)
    {
        Vector2D otherVector2D = otherVector as Vector2D;
        if (otherVector2D != null)
            return new Vector2D() { X = this.X + otherVector2D.X, Y = this.Y + otherVector2D.Y };

        Vector3D otherVector3D = otherVector as Vector3D;
        if (otherVector3D != null)
            return new Vector3D() { X = this.X + otherVector3D.X, Y = this.Y + otherVector3D.Y, Z = otherVector3D.Z };

        //handle other cases
    }
}


public class Vector3D : Vector
{
    public double X { get; set; }
    public double Y { get; set; }
    public double Z { get; set; }

    protected override Vector Add(Vector otherVector)
    {
        Vector2D otherVector2D = otherVector as Vector2D;
        if (otherVector2D != null)
            return new Vector3D() { X = this.X + otherVector2D.X, Y = this.Y + otherVector2D.Y, Z = this.Z };

        Vector3D otherVector3D = otherVector as Vector3D;
        if (otherVector3D != null)
            return new Vector3D() { X = this.X + otherVector3D.X, Y = this.Y + otherVector3D.Y, Z = this.Z + otherVector3D.Z };

        //handle other cases
    }
}

編集x2:

あなたの最新のコメントを考えると、おそらく内部配列/行列を維持し、一般的な行列計算を行う必要があります。サブクラスは、配列インデックスに対して X/Y/Z プロパティ ラッパーを公開できます。

public class Vector
{
    protected double[] Values;
    public int Length { get { return Values.Length; } }

    public static Vector operator +(Vector v1, Vector v2)
    {
        if (v1.Length != v2.Length)
        {
            throw new VectorTypeException("Vector Dimensions Must Be Equal");
        }
        else
        {
            //perform generic matrix addition/operation
            double[] newValues = new double[v1.Length];
            for (int i = 0; i < v1.Length; i++)
            {
                newValues[i] = v1.Values[i] + v2.Values[i];
            }

            //or use some factory/service to give you a Vector2D, Vector3D, or VectorND
            return new Vector() { Values = newValues };
        }
    }
}

public class Vector2D : Vector
{
    public double X
    {
        get { return Values[0]; }
        set { Values[0] = value; }
    }
    public double Y
    {
        get { return Values[1]; }
        set { Values[1] = value; }
    }
}


public class Vector3D : Vector
{
    public double X
    {
        get { return Values[0]; }
        set { Values[0] = value; }
    }
    public double Y
    {
        get { return Values[1]; }
        set { Values[1] = value; }
    }
    public double Z
    {
        get { return Values[2]; }
        set { Values[2] = value; }
    }
}

EDITx3: あなたの最新のコメントに基づいて、各サブクラスに演算子のオーバーロードを実装し、静的メソッド (基本 Vector クラスなど) で共有ロジックを実行し、特定のサブクラスを提供するためにどこかでスイッチ/ケース チェックを実行できると思います:

    private static Vector Add(Vector v1, Vector v2)
    {
        if (v1.Length != v2.Length)
        {
            throw new VectorTypeException("Vector Dimensions Must Be Equal");
        }
        else
        {
            //perform generic matrix addition/operation
            double[] newValues = new double[v1.Length];
            for (int i = 0; i < v1.Length; i++)
            {
                newValues[i] = v1.Values[i] + v2.Values[i];
            }

            //or use some factory/service to give you a Vector2D, Vector3D, or VectorND
            switch (newValues.Length)
            {
                case 1 :
                    return new Vector1D() { Values = newValues };
                case 2 :
                    return new Vector2D() { Values = newValues };
                case 3 :
                    return new Vector3D() { Values = newValues };
                case 4 :
                    return new Vector4D() { Values = newValues };
                //... and so on
                default :
                    throw new DimensionOutOfRangeException("Do not support vectors greater than 10 dimensions");
                    //or you could just return the generic Vector which doesn't expose X,Y,Z values?
            }
        }
    }

次に、サブクラスは次のようになります。

    public class Vector2D
    {
        public static Vector2D operator +(Vector2D v1, Vector2D v2)
        {
            return (Vector2D)Add(v1, v2);
        }
    }

    public class Vector3D
    {
        public static Vector3D operator +(Vector3D v1, Vector3D v2)
        {
            return (Vector3D)Add(v1, v2);
        }
    }

いくつかの重複がありますが、頭のてっぺんからコンパイラがこれを実行できるようにする方法がわかりません。

    Vector3 v1 = new Vector3(2, 2, 2);
    Vector3 v2 = new Vector3(1, 1, 1);
    var v3 = v1 + v2; //Vector3(3, 3, 3);
    Console.WriteLine(v3.X + ", " + v3.Y + ", " + v3.Z);

または他の次元の場合:

    Vector2 v1 = new Vector2(2, 2);
    Vector2 v2 = new Vector2(1, 1);
    var v3 = v1 + v2; //Vector2(3, 3, 3);
    Console.WriteLine(v3.X + ", " + v3.Y); // no "Z" property to output!
于 2012-06-13T22:53:51.253 に答える
0

operator+ が単にラッパーとして機能する Add() という抽象メソッドを持つことについてはどうですか? つまり、「v1.Add(v2) を返す」。これにより、Vector 以外のクラスがコードを制約できるインターフェイスを定義することもでき、数学のような操作を実行できるようになります (一般的なコードでは、任意の型の +、- などの演算子を表示/タッチできないため)。

ジェネリック メソッドでコーディングできる唯一のコンストラクターは、メソッド/型のジェネリック制約で指定する必要がある既定の (つまり、パラメーターのない) コンストラクターです。

于 2012-06-13T22:54:04.233 に答える
0

5 年後、私はまったく同じ問題を抱えていましたが、ベクトルではなく Ntuples と呼んでいただけです。これが私がしたことです:

using System;
using System.Collections.Generic;

  public class Ntuple{
    /*parent class
    has an array of coordinates
    coordinate-wise addition method
    greater or less than in dictionary order
    */
    public List<double> Coords = new List<double>();
    public int Dimension;

    public Ntuple(List<double> Input){
      Coords=Input;
      Dimension=Input.Count;
    }//instance constructor

    public Ntuple(){
    }//empty constructor, because something with the + overload?


   public static Ntuple operator +(Ntuple t1, Ntuple t2)
   {
     //if dimensions don't match, throw error
     List<double> temp = new List<double>();
     for (int i=0; i<t1.Dimension; i++){
       temp.Add(t1.Coords[i]+t2.Coords[i]);
     }
     Ntuple sum = new Ntuple(temp);
     return sum;
   }//operator overload +

   public static bool operator >(Ntuple one, Ntuple other){
     //dictionary order
     for (int i=0; i<one.Dimension; i++){
       if (one.Coords[i]>other.Coords[i]) {return true;}
     }
     return false;
   }
   public static bool operator <(Ntuple one, Ntuple other){
     //dictionary order
     for (int i=0; i<one.Dimension; i++){
       if (one.Coords[i]<other.Coords[i]) {return true;}
     }
     return false;
   }

  }//ntuple parent class



  public class OrderedPair: Ntuple{
    /*
    has additional method PolarCoords, &c
    */
    public OrderedPair(List<double> Coords) : base(Coords){}
    //instance constructor
    public OrderedPair(Ntuple toCopy){
      this.Coords=toCopy.Coords;
      this.Dimension=toCopy.Dimension;
    }

  }//orderedpair

  public class TestProgram{
    public static void Main(){
      List<double> oneCoords=new List<double>(){1,2};
      List<double> otherCoords= new List<double>(){2,3};


      OrderedPair one = new OrderedPair(oneCoords);
      OrderedPair another = new OrderedPair(otherCoords);
      OrderedPair sum1 = new OrderedPair(one + another);


      Console.WriteLine(one.Coords[0].ToString()+one.Coords[1].ToString());
      Console.WriteLine(sum1.Coords[0].ToString()+sum1.Coords[1].ToString());

      bool test = one > another;
      Console.WriteLine(test);
      bool test2 = one < another;
      Console.WriteLine(test2);
    }
  }


}//namespace ntuples
于 2017-05-29T18:56:34.750 に答える