【问题标题】:C# Abstract Class Operator OverloadC# 抽象类运算符重载
【发布时间】:2012-06-16 23:19:57
【问题描述】:

我有一个抽象类 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) 以另一种方式优雅地做到这一点?

【问题讨论】:

  • 你的派生类是什么样子的?
  • 假设VectorA + VectorB 都派生自Vector,结果会是什么?
  • 您需要继承Vector(这似乎主要是double 的数组)对我来说似乎很奇怪。您能解释一下您的层次结构吗?
  • 你能实现类似protected abstract Vector Add(Vector otherVector) 的方法吗?这样,您的操作员可以调用虚拟方法并允许您的子实现处理工作?
  • 旁注:在 + 运算符中进行反射会导致代码的性能非常不直观。考虑其他方法或对反射进行更清晰的缓存以避免意外的性能损失。

标签: c# inheritance operator-overloading abstract-class


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    您可以声明您的子类可以覆盖的实例级抽象方法:

    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 类会怎样?)并且可能处理空情况。此外,确保SubVector.Add 在交换操作方面的行为与SomeOtherSubVectorClass.Add 相同。

    编辑:根据您的其他 cmets,您可以这样:

    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!
    

    【讨论】:

    • 在我使用的情况下,向量只有在长度相同的情况下才能相互求和。我本质上是在尝试正确的矢量行为。但是因为所有维度向量都以相同的方式相加,只是参数数量不同,我只想写一次。
    • 啊,我在此评论后进行了编辑。也许你应该用索引数组来处理你的向量?做一些长度检查并根据需要以通用方式执行矩阵数学运算?
    • 这基本上就是我已经发布的内容。我感到困惑的是发生铸造/新对象创建的地方。如果返回对象是作为子类创建的,但是作为父类传出去的,能不能返回给子类呢?
    • 查看我上面的最后一次编辑。一些代码重复,基类中有一个奇怪的开关/案例。也许可以将该开关/外壳移至专用工厂。无论如何,不​​知道除此之外还能做些什么来保持对向量消费者的隐式输入。
    【解决方案3】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-11-13
      • 2016-07-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-22
      • 1970-01-01
      相关资源
      最近更新 更多