操作符重载位置和冗余
本文关键字:冗余 位置 重载 操作符 | 更新日期: 2023-09-27 18:13:25
我有多个结构体如下:Vector2, Vector3, Vector4。每个结构体都有为基本算术运算以及隐式和显式强制转换定义的操作符重载。
到目前为止,我已经在Vector4类中添加了所有可能的组合:public static Vector4 operator + (Vector4 v1, Vector4 v2) { return (new Vector4(...)); }
public static Vector4 operator + (Vector4 v1, Vector3 v2) { return (new Vector4(...)); }
public static Vector4 operator + (Vector3 v1, Vector4 v2) { return (new Vector4(...)); }
public static implicit operator Vector4 (Vector2 v) { return (new Vector4(v)); }
public static implicit operator Vector4 (Vector3 v) { return (new Vector4(v)); }
public static explicit operator Vector3 (Vector4 v) { return (new Vector3(v)); }
public static explicit operator Vector2 (Vector4 v) { return (new Vector2(v)); }
是否有关于哪个操作符更适合哪个结构的指导原则?我无法想象任何一种方式都会损害性能,但我很想知道,如果其他开发人员遇到这些代码,他们会更直观地看到什么。运算符组合的数量迅速增加到几十个。
顺便说一下,在其他类中复制这些操作符不会导致编译时错误。我没有检查将调用哪个实现,但这不是重点。如果每个类代表二维,三维和四维向量,我认为您应该可以减少一些代码。这是因为在不同维数的向量之间的向量算术的定义是多余的,只要你有必要的隐式上转换。因此,不需要像下面这样的操作符:
public static Vector4 operator + (Vector4 v1, Vector3 v2) { return (new Vector4(...)); }
public static Vector4 operator + (Vector3 v1, Vector4 v2) { return (new Vector4(...)); }
我还建议让低维向量处理到高维向量的上转换和从高维向量的下转换。这是因为向下转换剥离了信息,而如何进行转换的选择应该在"更有限"的结构体中。
因此,VectorI
结构体需要隐式向上转换到所有VectorI+J
,并显式向下转换到所有VectorI-J
结构体。此外,VectorI结构体需要实现它们自己的向量算法。但由于'I'只有值2、3和4,这意味着:
Vector2需要隐式转换到Vector3和Vector4,以及从Vector3和Vector4显式向下转换。
Vector3需要隐式转换到Vector4,以及从Vector4显式向下转换。
Vector4不需要转换
所有4个结构体都为自己实现了线性代数方法,仅在相同维数的向量之间。
我刚刚测试了这个方案,并添加了不同的Vector2, Vector3和Vector4结构体,与预期的隐式转换一样完成。
只是为加法做了一个快速的原型实现,所有的跨维度加法都像预期的那样工作:
public struct Vector2
{
public double x, y;
public Vector2(double x, double y)
{
this.x = x; this.y = y;
}
#region linear algebra
public static Vector2 operator +(Vector2 first, Vector2 second)
{
return new Vector2(first.x + second.x, first.y + second.y);
}
#endregion
#region conversions to/from higher dimensions
public static implicit operator Vector3(Vector2 v2)
{
return new Vector3(v2.x, v2.y, 0);
}
public static implicit operator Vector4(Vector2 v2)
{
return new Vector4(v2.x, v2.y, 0, 0);
}
public static explicit operator Vector2(Vector3 v3)
{
return new Vector2(v3.x, v3.y);
}
public static explicit operator Vector2(Vector4 v4)
{
return new Vector2(v4.x, v4.y);
}
#endregion
}
public struct Vector3
{
public double x, y, z;
public Vector3(double x, double y, double z)
{
this.x = x; this.y = y; this.z = z;
}
#region linear algebra
public static Vector3 operator +(Vector3 first, Vector3 second)
{
return new Vector3(first.x + second.x, first.y + second.y, first.z + second.z);
}
#endregion
#region conversions to/from higher dimensions
public static implicit operator Vector4(Vector3 v3)
{
return new Vector4(v3.x, v3.y, v3.z, 0);
}
public static explicit operator Vector3(Vector4 v4)
{
return new Vector3(v4.x, v4.y, v4.z);
}
#endregion
}
public struct Vector4
{
public double x, y, z, w;
public Vector4(double x, double y, double z, double w)
{
this.x = x; this.y = y; this.z = z; this.w = w;
}
#region linear algebra
public static Vector4 operator +(Vector4 first, Vector4 second)
{
return new Vector4(first.x + second.x, first.y + second.y, first.z + second.z, first.w + second.w);
}
#endregion
}
下面的测试代码可以正常工作:
public static class VectorHelper
{
public static void Test()
{
var v2 = new Vector2(5, 5);
var v3 = new Vector3(7, 7, 7);
var v4 = new Vector4(3, 3, 3, 3);
var res1 = v2 + v3;
Debug.Assert(res1.GetType().Name == "Vector3"); // No assert
var res2 = v3 + v4;
Debug.Assert(res2.GetType().Name == "Vector4"); // No assert
var res3 = v2 + v4;
Debug.Assert(res3.GetType().Name == "Vector4"); // No assert
Debug.Assert(res3.x == 8 && res3.y == 8 && res3.z == 3 && res3.w == 3); // No assert
}
}