这是正确的做法吗?

时间:2016-03-11 14:03:40

标签: c#

我刚刚编写了一个可变的Point类,它在使用C#6的新功能时尝试关注these guidelines

public class Point
    {
        public int X, Y;
        public static Point Zero => new Point(0, 0);
        public static Point Up => new Point(0, -1);
        public static Point Down => new Point(0, 1);
        public static Point Left => new Point(-1, 0);
        public static Point Right => new Point(1, 0);

        public Point(int x, int y)
        {
            X = x;
            Y = y;
        }

        public static Point operator +(Point a, Point b) => new Point(a.X + b.X, a.Y + b.Y);

        public static Point operator -(Point a) => new Point(-a.X, -a.Y);

        public static Point operator -(Point a, Point b) => a + -b;

        public override bool Equals(object o) => Equals(o as Point);

        public bool Equals(Point p) => (X == p?.X) && (Y == p?.Y);

        public override int GetHashCode() => X ^ Y;

        public static implicit operator string(Point a) => $"Point({a.X}, {a.Y})";

        public override string ToString() => this;
    }

这段代码评估为false

Point.Zero == Point.Zero

我完全清楚为什么会这样做,但问题是它应该评估为假吗?它对我来说并不合适。我的Point班级是否正确遵循指南?

编辑:我根本不知道它是否应该评估为假。我想知道人们认为它应该评估什么。

编辑2:如何判断某个类是否应该是可变的?

3 个答案:

答案 0 :(得分:3)

==除非被覆盖,否则不会自动调用equals。它将在其基类上调用object运算符,在您的情况下,它只是==object上的Point运算符使用引用相等性,因此您询问两个== 引用是否相等,在您的情况下它们不相同,因为您的静态方法每次都返回一个 new 实例。

因此:

  

它应该评估为假吗?

根据你如何撰写课程,是的,它应该。如果您不想希望评估为false,那么您需要更改内容。

  

我的Point类是否正确遵循指南?

您还没有覆盖Point,因此它将使用默认行为,该行为遵循指南。如果您使== 不可变,则覆盖Point.Zero == Point.Zero将是合适的。

如果您不想希望Equals返回false,可以采用以下方法:

  • 使用==代替Zero来暗示价值平等
  • 更改静态方法以每次返回相同的实例。有一个代表Up,Point等的实例,并返回它们而不是创建新实例。
  • struct更改为Point,默认情况下将使用值相等而不是引用相等。

请注意,如果您将struct更改为__asm__ __volatile__ ( "vldmia.64 %[data_addr]!, {d0-d1}\n\t" "vmov.f32 q12, #0.0\n\t" : [data_addr] "+r" (data_addr) : : "q0", "q12"); for(int n=0; n<10; ++n){ __asm__ __volatile__ ( "vadd.f32 q12, q12, q0\n\t" "vldmia.64 %[data_addr]!, {d0-d1}\n\t" : [data_addr] "+r" (data_addr), :: "q0", "q12"); } ,那么您也应该将其设为不可变。可变结构can be challenging to work with因为它们在传递时被复制。因此,如果更改属性值,则需要更改结构的副本的属性值。

答案 1 :(得分:1)

也许这会澄清。这段代码:

 public static Point Zero => new Point(0, 0);

是否相当于此代码:

 public static Point Zero
 {
     get { return new Point(0, 0); }
 }

这里很明显,每次调用Point.Zero时都会创建一个新的Point实例。 ==运算符默认执行引用检查,因为它们是Point的两个单独实例,所以比较失败。

您可以覆盖==运算符,类似于覆盖+, - 运算符的方式:

public static bool operator ==(Point a, Point b) => a.X == b.X && a.Y == b.Y;

public static bool operator !=(Point a, Point b) => a.X != b.X || a.Y != b.Y;

答案 2 :(得分:1)

首先,Zero属性每次访问时都会创建一个新点。所以当你比较这些时:

Point.Zero == Point.Zero

您实际上正在比较两个完全不同的实例!并且==仅比较2点内存中的地址,这就是为什么它是错误的。

现在已经开始了,让我们看看我们如何才能实现 true !实际上有很多方法。

  1. 使用Equals方法代替==运算符
  2. 重载课程中的==运算符
  3. 使Point结构化。 (注意:这将需要对现有的运算符重载进行一些更改,因为struct是值类型)
  4. 实际上,如果你想让它返回true,为什么不写1 == 1? XD
  5. 我看到你想要我们的一些建议,不是吗?以下是一些:

    1. 来吧,谁想要一个可变课?
    2. 如果您希望它是可变的,请不要覆盖HashCode
    3. 您的“常量”声明非常严重。我甚至不确定它们是否应该是常数。它应该是这样的,你知道:

      private static Point zero = new Point(0,0); public static Point Zero {     得到{return zero;} }

    4. 或只是简单地说:

      public static readonly Point Zero = new Point(0, 0);
      
      1. 我认为结构更适合Point。毕竟这是一个“结构”。甚至System.Windows.Forms.Point也是一个结构。