【问题标题】:Should I Overload == Operator?我应该重载 == 运算符吗?
【发布时间】:2010-12-11 02:10:47
【问题描述】:

== 运算符在 C# 中如何真正发挥作用?如果它用来比较A类的对象,它会尝试匹配A的所有属性,还是会寻找指向相同内存位置的指针(或者可能别的)?

让我们创建一个假设的例子。我正在编写一个使用 Twitter API 的应用程序,它有一个 Tweet 类,它具有单个推文的所有属性:文本、发件人、日期和时间、来源等。如果我想比较 Tweet 类的对象是否相等,我可以使用:

Tweet a, b;
if (a == b)
{
//do something...
}

是否会检查 ab 之间 Tweet 类的所有属性的等价性

如果不是,正确的方法是重载 == 运算符以显式检查所有字段的等价性吗?

更新:根据前两个答案,我是否正确地假设:

  • 如果== 运算符或Equals 方法没有为类重载,则使用object 类的== 运算符。
  • object 类的== 运算符检查内存位置是否相等。
  • 我必须重载 == 运算符或 Equals 方法才能完成这项任务。
  • 在重载中,我必须手动检查属性中的等价性,所以没有办法半自动地进行,比如在循环中,对吧?

更新 #2: Yuriy 发表评论说,可以使用 reflection 检查 == 运算符中的属性等价性。如何才能做到这一点?你能给我一些示例代码吗?谢谢!

【问题讨论】:

  • 在重载运算符之前,请考虑一下:当涉及运算符重载时,我通常的经验法则是问自己这个问题:“这是否具有常识?任何其他开发人员会立即知道操作员甚至不看实现(代码)?”如果答案不是“100% 是”,请不要这样做。如果是,那绝对是,去吧。

标签: c# operators operator-overloading equals-operator type-equivalence


【解决方案1】:

对于引用类型,== 运算符和 Equals() 方法的默认实现将简单地检查两个对象是否具有相同的引用,因此是相同的实例。

如果你想检查两个不同对象的内容是否相等,那么你必须自己编写代码来做这件事,一种或另一种方式。可以使用反射(MbUnit 测试框架在这些方面做了一些事情),但是性能损失很大,而且很有可能它不会完全按照你的预期做,你应该实现==EqualsGetHashCode 手动。

【讨论】:

  • +1 表示注意到基于反射的解决方案的性能和复杂性后果。
  • @GraemeF,性能方面,仍然有可能在 getter 上使用带有编译表达式树的缓存策略(如果你真的这么认为,甚至可以使用一些 IL)来检查字段和属性在运行时。大多数 ORM 都在使用这种方式,它可以非常快(虽然比纯硬编码访问慢一点)。
【解决方案2】:

MSDN 有一个很好的example 怎么做:

   public override bool Equals(object o) 
   {
      try 
      {
         return (bool) (this == (DBBool) o);
      }
      catch 
      {
         return false;
      }
   }

然后你重载 == 和 !=:

// Equality operator. Returns dbNull if either operand is dbNull, 
   // otherwise returns dbTrue or dbFalse:
   public static DBBool operator ==(DBBool x, DBBool y) 
   {
      if (x.value == 0 || y.value == 0) return dbNull;
      return x.value == y.value? dbTrue: dbFalse;
   }

   // Inequality operator. Returns dbNull if either operand is
   // dbNull, otherwise returns dbTrue or dbFalse:
   public static DBBool operator !=(DBBool x, DBBool y) 
   {
      if (x.value == 0 || y.value == 0) return dbNull;
      return x.value != y.value? dbTrue: dbFalse;
   }

别忘了重载 GetHash 方法。

编辑:

我编写了以下在比较中使用反射的快速示例。这必须更全面,如果人们希望我这样做,我可能会尝试在上面写一个博客:

public class TestEquals
{
    private int _x;
    public TestEquals(int x)
    {
        this._x = x;
    }

    public override bool Equals(object obj)
    {
        TestEquals te = (TestEquals)obj;
        if (te == null) return false;

        foreach (var field in typeof(TestEquals)
            .GetFields(BindingFlags.NonPublic | BindingFlags.Instance))
        {
            if (!field.GetValue(this).Equals(field.GetValue(te)))
                return false;
        }
        return true;
    }
}

【讨论】:

  • +1 用于反射解决方案(这是您第二次在我之前发布一分钟 :)
  • 您将如何使用这样的基于反射的方法来实现 GetHashCode()?
  • @John:为什么不结合使用反射和 Jon Skeet 的回答:stackoverflow.com/questions/263400#263416
  • @pst:这是一个示例,假设您有一个从 Sql Server 为 LinqToSql 生成的类。它没有 Equals,但您不确定所有字段将是什么,并且不想在每次生成时添加新字段。因此,您在反映字段的部分类中覆盖 Equals,可能会排除 id,然后您就完成了。
  • 反射应该谨慎使用,并且仅在您不知道所涉及的类型时使用。不要将其用作实现 Equals 的通用解决方案。不要偷懒! :)
【解决方案3】:

正确的方法是重载 Tweet 类的 equals 方法以及 == 运算符,如 here 所述。

【讨论】:

  • 那么我必须手动检查每个属性的等价性吗?有没有更快的方法来做到这一点,比如以某种方式循环?
  • @Maxim:您可以使用反射来比较所有字段。你有几个领域?注意 SRP。
  • 您可以使用反射对您的所有内部属性运行 equals 函数。
  • 我在 C# 中不知道。 Java IDE允许你完全自动为一个类生成equals方法代码,也许Visual Studio有类似的东西?
  • @Yuriy @Am 你能给我一些示例代码吗?谢谢!
【解决方案4】:

这会检查 a 和 b 之间的 Tweet 类的所有属性的等价性吗?

没有

如果不是,正确的方法是重载 == 运算符以显式检查所有字段的等价性吗?

您可以重载 == 运算符,或重载 Equals 函数。

编辑

@Yuriy 给出了一个很好的例子来兼容所有非公共变量。由于我也写了一个例子,这里是(我的比较属性)

class TwitterItem
{
    private string myValue = "default value";
    public string Value1
    {
        get { return myValue; }
        set { myValue = value; }
    }
    public string Value2
    {
        get { return myValue; }
        set { myValue = value; }
    }
    public string Value3
    {
        get { return myValue; }
        set { myValue = value; }
    }

    public override bool Equals(object obj)
    {
        if (base.Equals(obj)) return true;

        Type type = typeof(TwitterItem);
        PropertyInfo[] properties = type.GetProperties();

        foreach (PropertyInfo property in properties)
        {
            if (false == property.GetValue(this, null).Equals(property.GetValue(obj, null)))
                return false;
        }

        return true;
    }
}

【讨论】:

  • 非常感谢,这真的很有帮助!
【解决方案5】:

您可以使用反射比较属性:

var a = new Entity() { Name = "test", ID = "1" };
var b = new Entity() { Name = "test", ID = "1" };
var c = new Entity() { Name = "test", ID = "2" };
System.Diagnostics.Debug.WriteLine(a.Equals(b));//Returns true
System.Diagnostics.Debug.WriteLine(a.Equals(c));//Returns false


public class Entity
{
    public string Name { get; set; }
    public string ID { get; set; }
    public override bool Equals(object obj)
    {
        var t = obj.GetType();
        foreach (var p in t.GetProperties())
        {
            if (t.GetProperty(p.Name).GetValue(obj, null) != t.GetProperty(p.Name).GetValue(this, null))
                return false;
        }
        return true;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-06
    • 1970-01-01
    • 2010-09-08
    • 1970-01-01
    相关资源
    最近更新 更多