在C#中实现这3个类的最佳方法:向量,方向(单位向量),Point

时间:2009-05-22 11:42:25

标签: c# class struct

  • 所有点都是矢量,所有矢量都是点。
  • 所有方向都是向量,并非所有向量都是方向(这不应该意味着不允许双向转换)。

我希望所有运算符都被覆盖一次,因为它们完全相同。在C ++中我可以定义类Vector {float x,y,z; },并且做typedef Point = Vector,typedef Direction = Vector;在C#中没有等价物(“使用Point = Vector;”很糟糕,因为你必须将它放在你使用的每一个文件中,并且它不是由编译器强制执行的。)

我试图定义3个不同的类并覆盖每个类的运算符,然后进行隐式类型转换,这会使代码运行得更慢等等。

我尝试定义只是Vector,然后点:向量和方向:向量,这样我只编写一次运算符但是我不能做隐式类型转换Point< - >矢量或方向< - >矢量。

我可以简单地定义Vector类并在任何地方使用它,但这会产生模糊性,因为天气变量应该是空间中的位置(Point),空间中的相对位置(Vector)或单位向量(方向)。例如功能:

Vector GetOrthogon(Vector a, Vector b) {
    // ....
}

你无法知道它是否期望任何向量或单位向量。在C ++中你可以这样做,为什么不在C#?

注意 :如果可能的话,拥有结构而不是类是理想的。

6 个答案:

答案 0 :(得分:10)

数学上,点是矢量。太空中没有绝对点。点被定义为来自某个任意来源的向量。所以,我对点和点之间的差异使用了矢量。

因为方向是单位向量,所以也不需要区分。这就像尝试为整数1和其他整数定义不同的静态类型。所以我使用向量来表示两个方向和点之间的差异。

因此,定义一个Vector类型。它会让你的生活更轻松,因为你会有更少的类和重载的操作符/函数来编写和测试,并且在数学上会更“纯粹”(如果这对你很重要)。

答案 1 :(得分:8)

在纯粹的层面上,我认为VectorPoint 相同,代数是:

  • Point + Vector => Point(翻译)
  • Vector + Vector => Vector(另外)
  • Point + Point(未定义)

我将有2个隐式(它们在逻辑上等价)或显式(否则)转换运算符的不可变结构。我可能没有Direction结构,但是......我可能在Direction上有一个Vector属性,可以将其缩放到单位,但这就是它... < / p>


我已经删除了原始VectorPoint以显示相互作用;我没有填写所有Point(因为它更简单):

public struct Point {
    public Point(double x, double y) : this() { X = x; Y = y; }
    public double X { get; private set; }
    public double Y { get; private set; }

    // and more ;-p
}

public struct Vector : IEquatable<Vector> {
    public Vector(double x, double y) : this() { X = x; Y = y; }
    public Vector AsUnit() {
        if (X == 0 && Y == 0) throw new InvalidOperationException();
        if (X == 0) return new Vector(0, X > 0 ? 1 : -1);
        if (Y == 0) return new Vector(Y > 0 ? 1 : -1, 0);
        double sqr = Math.Sqrt((X * X) + (Y * Y));
        return new Vector(X / sqr, Y / sqr);
    }
    public double X { get; private set; }
    public double Y { get; private set; }

    public static  explicit operator Point(Vector vector) {
        return new Point(vector.X, vector.Y);
    }
    public static explicit operator Vector(Point point) {
        return new Vector(point.X, point.Y);
    }
    public override string ToString() {
        return "(" + X.ToString() + "," + Y.ToString() + ")";
    }
    public override int GetHashCode() {
        return 17 * X.GetHashCode() + Y.GetHashCode();
    }
    public override bool Equals(object obj) {
        return obj == null ? false : Equals((Vector)obj);
    }
    public bool Equals(Vector vector) {
        return X == vector.X && Y == vector.Y;
    }
    public static bool operator ==(Vector a, Vector b) {
        return a.X == b.X && a.Y == b.Y;
    }
    public static bool operator !=(Vector a, Vector b) {
        return a.X != b.X || a.Y != b.Y;
    }
    public static Point operator +(Point point, Vector vector) {
        return new Point(point.X + vector.X, point.Y + vector.Y);
    }
    public static Point operator -(Point point, Vector vector) {
        return new Point(point.X - vector.X, point.Y - vector.Y);
    }
    public static Vector operator +(Vector a, Vector b) {
        return new Vector(a.X + b.X, a.Y + b.Y);
    }
    public static Vector operator -(Vector a, Vector b) {
        return new Vector(a.X - b.X, a.Y - b.Y);
    }
}

答案 2 :(得分:6)

.Net&gt; = 3.0中已有PointVector个实施。不幸的是System.Windows.Media.Media3D,因为它们是WPF 3D API的一部分。

这些现有的类确实有一些有用的属性,值得研究(其他人已经提到过):

  • Point [+|-] Vector = Point
  • Vector [+|-] Vector = Vector
  • Point - Point = Vector
  • Vector具有执行点和交叉产品的静态方法
  • Vector.Normalise()vector.Negate()按照他们的说法行事

也许您可以检查(甚至扩展)这些以供自己使用?

答案 3 :(得分:3)

我建议不要同时使用VectorPoint类型。尽管它们在数学上有点不同,但这种区别通常不会在编程上下文中提供任何优势。此外,您可以将点视为从原点到点的位置的矢量。

我强烈建议使用值类型而不是引用类型,因为您通常使用向量,因为您使用实数,因此您需要与float(pass-by-value)相同的语义。如果选择值类型,请记住通常最好使它们不可变。

我还建议不要使用Direction类型,而更喜欢使用标准化向量。而不是您的三种VectorPointDirection类型,只有Vector就足够了(如果它足够通用)。

至于实施参考,您可以查看XNA's Vector3 structure

答案 4 :(得分:1)

如果向量是点,而点是向量,则可能它们应该是相同的类(或相同类型的结构)。

或者,如果你想要他们......

  • 具有相同方法和运算符的相同实现
  • 有不同的名字和不同的类型

...然后使用受保护的构造函数定义基类中的方法和运算符,并将Point和Vector定义为此基类的叶子类,以便它们继承其功能。

如果您不能使用继承等来完成您想要的操作,那么将操作符定义复制并粘贴到几个类中,因为a)它不是维护问题,因为它们不太可能改变b)你说你有一个合理的理由,即“我正在做一个光线跟踪器,因此速度至关重要”。

  

你可能会说它没关系,因为它们都是向量,但在C ++中你很容易有区别,为什么不用C#?

你也可以在C#中区分:C#支持定义不同的类型等。你可以用C ++做的两件事你不能在C#中使用鸭子模板和全局函数,这可能在这里有用但不是必要的,如果您愿意使用继承或复制并粘贴常用功能。

答案 5 :(得分:1)

我建议将其定义为:

public abstract class VectorBase {
    public int X { get; private set; }
    public int Y { get; private set; }

    public VectorBase(int x, int y) {
        this.X = x;
        this.Y = y;
    }

    public VectorBase(VectorBase copy) : this(copy.X, copy.Y) {
        // creates a vector with the same x, y
    }

    public static Vector operator +(VectorBase left, VectorBase right) {
        return new Vector(left.X + right.X, left.Y + right.Y);
    }

    public static Vector operator -(VectorBase left, VectorBase right) {
        return new Vector(left.X - right.X, left.Y - right.Y);
    }
}

public class Vector : VectorBase {
    public Vector(VectorBase v) : base(v) { }
    public Vector(int x, int y) : base(x, y) { }
}

public class Point : VectorBase {
    public Point(VectorBase v) : base(v) { }
    public Point(int x, int y) : base(x, y) { }

    public static implicit operator Vector(Point p) {
         return new Vector(p);
    }

    public static implicit operator Point(Vector v) {
         return new Point(v);
    }
}

public class Direction : VectorBase {
    public Direction(VectorBase v) : base(v) { }
    public Direction(int x, int y) : base(x, y) { }

    public static implicit operator Vector(Direction d) {
         return new Vector(d);
    }

    public static implicit operator Direction(Vector v) {
         return new Direction(v);
    }

    // implementation of *, any other stuff you need
}

注意事项:

  • 此实施是immutable
  • 可以从任何其他类型添加/减去任何类型 - VectorPointDirection,从而产生Vector
  • Direction可以与矢量相乘。
  • 它们是隐式可互换的 - 您可以通过将implicit operator更改为explicit operator来轻松地将其更改为明确可互换。