2012-06-13 22 views
6

私は、+、 - 、*などの演算子をオーバーロードしたい抽象クラスVectorを持っています。
私は派生クラスでこれらを使用してオブジェクトを取得できるようにしたい呼び出し元のオブジェクトと同じ型の戻り値を返します。
(次のように簡単に、)私は、ジェネリックと試みたが、私はそれを行うための法的な方法を見つけることができませんでした:C#抽象クラス演算子オーバーロード

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)これを別の方法でエレガントに行う方法は何ですか?

+0

派生クラスはどのように見えますか? – JotaBe

+0

'Vector'(これは主に' double'の配列であるようです)をサブクラス化する必要があることは奇妙に思えます。あなたの階層についてもう少し説明できますか? – dlev

+0

'VectorA + VectorB'の結果は' Vector'から派生したものとすればどうなりますか? –

答えて

10

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

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.AddSomeOtherSubVectorClass.Addと同じに動作することを確認してください。

EDIT:あなたの他のコメントをもとに、あなたはとても何かのようでした:

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 
    } 
} 

EDITx2:

最新のコメントを考えると、おそらくあなただけの内部配列/行列を維持しなければならないだけでジェネリック行います行列の数学。あなたのサブクラスが配列なインデックスに対する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! 
+0

私が使用しているケースでは、ベクトルは同じ長さの場合に限り、互いに合計できます。私は基本的には適切なベクターの動作を試みています。しかし、すべての次元ベクトルが同じ方法で追加されるので、引数の数が違うだけで、一度だけ書きたいと思います。 – 3Pi

+0

ああ、このコメントの後に編集しました。おそらく、インデックス付き配列でベクトルを扱うべきでしょうか?いくつかの長さチェックを行い、一般的な方法で必要に応じて行列演算を実行しますか? –

+0

それは本質的に私がすでに持っているものであり、投稿されたものです。私が混乱しているところは、キャスト/新しいオブジェクトの作成が行われる場所です。戻りオブジェクトが子クラスとして作成され、親クラスとして渡された場合、それを子クラスに戻すことはできますか? – 3Pi

0

どうなる約10いくつかの重複が、私は、コンパイラはこれを行うことができるように私の頭の上から、それを回避する方法が表示されませんAdd()という抽象メソッドは、演算子+は単にラッパーとして機能しますか?すなわち、 "return v1.Add(v2)"とする。これにより、Vector以外のクラスがコードを制約することができるインターフェイスを定義することも可能になり、数式のような操作が可能になります(汎用コードでは+、 - などのタイプのオペレータを表示/

ジェネリックメソッドでコーディングできるコンストラクターは、メソッド/タイプのジェネリック制約で指定する必要があるデフォルト(つまり、パラメーターなし)コンストラクターです。

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 
関連する問題