2012-06-13 33 views
6

Tengo una clase abstracta, Vector, que me gustaría sobrecargar los operadores +, -, *, etc.
Quiero que todas las clases derivadas puedan usarlas y obtener un objeto de vuelta con el mismo tipo que el objeto que llama.
me trataron con los genéricos, (según se indica, en breve), pero no pude encontrar una forma legal de hacerlo:C# Clase abstracta Sobrecarga del operador

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

Luego trató de hacerlo simplemente utilizando la clase base:

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

Si este método se pasa en dos objetos secundarios, debe realizar la operación en ellos y devolver un nuevo objeto del mismo patrimonio.

El problema que encontré con esto es que no puedo exigir que todas las clases secundarias tengan un constructor con la firma adecuada, y no puedo llamar al constructor base para crear el objeto.

¿Cuáles son las formas de (a) hacer alguno de estos trabajos, o (b) hacerlo elegantemente de otra manera?

+0

¿Cómo son sus clases derivadas? – JotaBe

+0

Me parece extraño que necesite subclasificar 'Vector' (que parece ser principalmente una matriz de' double'.) ¿Podría explicar un poco más acerca de su jerarquía? – dlev

+0

¿Cuál sería el resultado de 'VectorA + VectorB', suponiendo que ambos derivan de' Vector'? –

Respuesta

10

Se puede declarar métodos abstractos a nivel de instancia, que la subclase puede anular:?

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

podría encontrarse con algunos problemas, especialmente con múltiples subclases (Will SubVector tiene que saber cómo agregar con SomeOtherSubVectorClass ¿Qué pasa si se agregan ThirdVectorType clase?) Y quizás manejando casos nulos. Además, asegúrese de que SubVector.Add se comporte igual que SomeOtherSubVectorClass.Add cuando se trata de operaciones conmutativas.

EDIT: en base a sus otros comentarios, usted podría así que algo como:

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:

Dada su último comentario, tal vez su sólo debe mantener un conjunto de discos/matriz interna y sólo hacer genérica matemáticas matriciales Sus subclases pueden exponer envoltorios X/Y/Z es una propiedad frente a los índices del array:

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: Basado en su último comentario, supongo que se podría aplicar sobrecargas de operadores en cada subclase, ver la lógica compartida en un método estático (digamos en la clase Vector de base), y en algún lugar hacer una verificación de switch/case para proporcionar una subclase específica:

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

a continuación, sus subclases tendrían:

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

Algunos duplicación, pero no veo una forma de evitar que la parte superior de la cabeza para permitir que el compilador para hacer esto:

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

o para otras dimensiones:

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

En el caso que estoy usando, los vectores solo pueden sumarse si tienen la misma longitud. En esencia, estoy intentando un comportamiento vectorial adecuado. Pero como todos los vectores de dimensión se agregan de la misma manera, solo con un número diferente de argumentos, solo quiero escribirlo una vez. – 3Pi

+0

Ahh, lo edité después de este comentario. ¿Quizás entonces debería tratar sus vectores con matrices indexadas? ¿Hace algunas verificaciones de longitud y realiza las matemáticas matriciales según sea necesario de forma genérica? –

+0

Eso es esencialmente lo que ya he publicado. Donde me confundo es el bit donde se produce la creación del objeto de fundición/nuevo objeto. Si el objeto de retorno se crea como una clase secundaria, pero se transfiere como una clase principal, ¿se puede devolver a la clase secundaria? – 3Pi

0

¿Qué hay de tener un método abstracto llamado Add() que operador + simplemente actúa como un contenedor para? es decir, "return v1.Add (v2)".Esto también le permitiría definir interfaces a las que las clases que no pertenecen a Vector pueden restringir su código, permitiendo realizar operaciones de tipo matemático (ya que el código genérico no puede ver/tocar operadores como +, -, etc. para ningún tipo).

El único constructor con el que puede codificar en un método genérico es el constructor predeterminado (es decir, sin parámetros), que debe especificar en las restricciones genéricas para el método/tipo.

0

Cinco años después tuve exactamente el mismo problema, solo que los llamaba Ntuples, no vectores. Esto es lo que hice:

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