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

如果在两个子对象中传递此方法,它应该对它们执行操作,并返回相同继承的新对象。

我遇到的问题是我无法强制所有此类子类必须具有具有适当签名的构造函数,并且我无法调用基本构造函数来创建对象。

有什么方法可以(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必须知道如何添加 with SomeOtherSubVectorClass?如果添加ThirdVectorType类怎么办?)并且可能处理空情况。此外,确保其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

如果有一个名为 Add() 的抽象方法,operator+ 只是作为一个包装器呢?即,“返回 v1.Add(v2)”。这也将使您能够定义非 Vector 类可以约束其代码的接口,从而能够执行类似数学的操作(因为通用代码无法看到/触摸任何类型的 +、- 等运算符)。

您可以在泛型方法中使用的唯一构造函数是默认(即无参数)构造函数,您必须在方法/类型的泛型约束中指定它。

于 2012-06-13T22:54:04.233 回答
0

五年后,我遇到了完全相同的问题,只是我称它们为 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 回答