【问题标题】:Define how a comparison operator is applied to a type?定义如何将比较运算符应用于类型?
【发布时间】:2013-08-14 13:09:27
【问题描述】:

如何定义比较运算符是否以及如何应用于我的类型的操作数?

【问题讨论】:

    标签: c# comparison-operators icomparable


    【解决方案1】:

    您使用 CompareTo 方法实现 IComparable interface

    要使用所有的运算符,试试这个:

    public sealed class Foo : IEquatable<Foo>, IComparable<Foo>
    {
        public static int Compare(Foo first, Foo second)
        {
            if (Object.ReferenceEquals(first, null))
                return (Object.ReferenceEquals(second, null) ? 0 : -1);
    
            return first.CompareTo(second);
        }
        public static bool operator==(Foo first, Foo second)
        {
            return Object.Equals(first, second);
        }
        public static bool operator!=(Foo first, Foo second)
        {
            return !Object.Equals(first, second);
        }
        public static bool operator<(Foo first, Foo second)
        {
            return Foo.Compare(first, second) < 0;
        }
        public static bool operator >(Foo first, Foo second)
        {
            return Foo.Compare(first, second) > 0;
        }
        public static bool operator <=(Foo first, Foo second)
        {
            return Foo.Compare(first, second) <= 0;
        }
        public static bool operator >=(Foo first, Foo second)
        {
            return Foo.Compare(first, second) >= 0;
        }
    
        private string bar;
    
        public string Bar
        {
            //getter and setter
        }
    
       public bool Equals(Foo other)
        {
            if (Object.ReferenceEquals(other, null))
                return false;
            if (Object.ReferenceEquals(other, this)) //Not mandatory
                return true;
    
            return String.Equals(this.foo, other.foo);
        }
        public int CompareTo(Foo other)
        {
            if (Object.ReferenceEquals(other, null))
                return 1;
            if (Object.ReferenceEquals(other, this)) //Not mandatory
                return 0;
    
            return String.Compare(this.bar, other.bar);
        }
        public override bool Equals(object obj)
        {
            return this.Equals(obj as Foo);
        }
        public override int GetHashCode()
        {
            return this.bar == null ? 0 : this.bar.GetHashCode();
        }
    }
    

    一个很好的教程:http://winsharp93.wordpress.com/2009/06/28/implementing-icomparablet-iequatablet-and-the-equality-members/

    由于您知道 IComparable 接口,因此需要实现 IEquatable 接口,您可以通过使用此(示例)来判断 yourClass 的两个实例是否可比较:

    if (yourClass is IEquatable<T> && yourClass2 is IEquatable<T> && yourClass is IComparable<T> && yourClass2 is IComparable<T>) //T is the same type
    {
    yourClass <= yourClass2;
    }
    

    【讨论】:

    • Operator '&lt;=' cannot be applied to operands of type 'System.IComparable' and 'System.IComparable' 为什么?
    • 要做到这一点,您需要 IEquatable 接口 - 更新了我的答案。
    • 如果出现这种情况怎么办:public class Base&lt;T&gt; : IEquatable&lt;Base&lt;T&gt;&gt;T foo;IComparable barif (foo is IComparable) if (((IComparable) foo) &lt;= bar) doSomething();
    • 想通了,用了if (foo is IComparable) if (!((IComparable) foo).CompareTo(bar) &gt; 0) doSomething();
    【解决方案2】:

    你可以overload operators为你的类型,例如

    public class MyComparable
    {
        public static bool operator <(MyComparable left, MyComparable right)
        {
        // other things...
    }
    

    允许您这样做:

    MyComparable c1 = // something
    MyComparable c2 = // something
    if (c1 < c2)
        // something
    

    (在这种情况下,实现IComparable&lt;MyComparable&gt; 也可能有意义。

    【讨论】:

      【解决方案3】:

      实现IComparableIComparable&lt;T&gt;IEquatable&lt;T&gt; 并覆盖object.Equals(object)object.GetHashCode() 和各种“标准”运算符==!=&gt;&lt; 的简单类,&gt;=&lt;=

      注意object.ReferenceEquals(object, object)的使用,以免触发StackOverflowException。这是因为我们重载了==!= 运算符并基于MyClass.Equals(MyClass),所以MyClass.Equals(MyClass) 显然不能使用它们。事实上,一个常见的错误是在

      bool Equals(MyClass other)
      {
          if (other == null)
          {
          }
      }
      

      嘘!不能那样做。因为if (other == null)会递归调用other.Equals((MyClass)null)。你可以做的是:if (((object)other) == null),因为在 C# 中运算符不能是virtual,所以这里我们使用object 类的==

      InnerEqualsInnerCompareTo 存在,因此如果调用了 Equals(object)CompareTo(object),则不得进行两次 null 检查。

      public class MyClass : IComparable<MyClass>, IComparable, IEquatable<MyClass>
      {
          public int MyInt1 { get; set; }
          public int MyInt2 { get; set; }
      
          public int CompareTo(MyClass other)
          {
              if (object.ReferenceEquals(other, null))
              {
                  return 1;
              }
      
              return this.InnerCompareTo(other);
          }
      
          int IComparable.CompareTo(object obj)
          {
              // obj is object, so we can use its == operator
              if (obj == null)
              {
                  return 1;
              }
      
              MyClass other = obj as MyClass;
      
              if (object.ReferenceEquals(other, null))
              {
                  throw new ArgumentException("obj");
              }
      
              return this.InnerCompareTo(other);
          }
      
          private int InnerCompareTo(MyClass other)
          {
              // Here we know that other != null;
      
              if (object.ReferenceEquals(this, other))
              {
                  return 0;
              }
      
              int cmp = this.MyInt1.CompareTo(other.MyInt1);
      
              if (cmp == 0)
              {
                  cmp = this.MyInt2.CompareTo(other.MyInt2);
              }
      
              return cmp;
          }
      
          public override bool Equals(object obj)
          {
              // obj is object, so we can use its == operator
              if (obj == null)
              {
                  return false;
              }
      
              MyClass other = obj as MyClass;
      
              if (object.ReferenceEquals(other, null))
              {
                  return false;
              }
      
              return this.InnerEquals(other);
          }
      
          public bool Equals(MyClass other)
          {
              if (object.ReferenceEquals(other, null))
              {
                  return false;
              }
      
              return this.InnerEquals(other);
          }
      
          private bool InnerEquals(MyClass other)
          {
              // Here we know that other != null;
      
              if (object.ReferenceEquals(this, other))
              {
                  return true;
              }
      
              return this.MyInt1 == other.MyInt1 && this.MyInt2 == other.MyInt2;
          }
      
          public override int GetHashCode()
          {
              unchecked
              {
                  // From http://stackoverflow.com/a/263416/613130
                  int hash = 17;
                  hash = hash * 23 + this.MyInt1;
                  hash = hash * 23 + this.MyInt2;
                  return hash;
              }
          }
      
          public static bool operator==(MyClass a, MyClass b)
          {
              if (object.ReferenceEquals(a, null))
              {
                  return object.ReferenceEquals(b, null);
              }
      
              return a.Equals(b);
          }
      
          // The != is based on the ==
          public static bool operator!=(MyClass a, MyClass b)
          {
              return !(a == b);
          }
      
          public static bool operator>(MyClass a, MyClass b)
          {
              if (object.ReferenceEquals(a, null))
              {
                  return false;
              }
      
              return a.CompareTo(b) > 0;
          }
      
          // The <, >=, <= are all based on the >
          public static bool operator <(MyClass a, MyClass b)
          {
              return b > a;
          }
      
          public static bool operator >=(MyClass a, MyClass b)
          {
              //return !(a < b);
              //We short-circuit the <operator, because we know how it's done
              return !(b > a);
          }
      
          public static bool operator <=(MyClass a, MyClass b)
          {
              return !(a > b);
          }
      }
      

      这是struct 类型的变体。短得多,因为几乎所有的object.ReferenceEquals(object, object) 都消失了。值类型不能为空。

      public struct MyStruct : IComparable<MyStruct>, IComparable, IEquatable<MyStruct>
      {
          public int MyInt1 { get; set; }
          public int MyInt2 { get; set; }
      
          public int CompareTo(MyStruct other)
          {
              return this.InnerCompareTo(other);
          }
      
          int IComparable.CompareTo(object obj)
          {
              if (obj == null)
              {
                  return 1;
              }
      
              if (!(obj is MyStruct))
              {
                  throw new ArgumentException("obj");
              }
      
              MyStruct other = (MyStruct)obj;
      
              return this.InnerCompareTo(other);
          }
      
          private int InnerCompareTo(MyStruct other)
          {
              int cmp = this.MyInt1.CompareTo(other.MyInt1);
      
              if (cmp == 0)
              {
                  cmp = this.MyInt2.CompareTo(other.MyInt2);
              }
      
              return cmp;
          }
      
          public override bool Equals(object obj)
          {
              if (obj == null)
              {
                  return false;
              }
      
              if (!(obj is MyStruct))
              {
                  throw new ArgumentException("obj");
              }
      
              MyStruct other = (MyStruct)obj;
      
              return this.InnerEquals(other);
          }
      
          public bool Equals(MyStruct other)
          {
              return this.InnerEquals(other);
          }
      
          private bool InnerEquals(MyStruct other)
          {
              return this.MyInt1 == other.MyInt1 && this.MyInt2 == other.MyInt2;
          }
      
          public override int GetHashCode()
          {
              unchecked
              {
                  // From http://stackoverflow.com/a/263416/613130
                  int hash = 17;
                  hash = hash * 23 + this.MyInt1;
                  hash = hash * 23 + this.MyInt2;
                  return hash;
              }
          }
      
          // The != is based on the ==
          public static bool operator ==(MyStruct a, MyStruct b)
          {
              return a.Equals(b);
          }
      
          public static bool operator !=(MyStruct a, MyStruct b)
          {
              return !(a == b);
          }
      
          public static bool operator >(MyStruct a, MyStruct b)
          {
              return a.CompareTo(b) > 0;
          }
      
          // The <, >=, <= are all based on the >
          public static bool operator <(MyStruct a, MyStruct b)
          {
              return b > a;
          }
      
          public static bool operator >=(MyStruct a, MyStruct b)
          {
              //return !(a < b);
              //We short-circuit the <operator, because we know how it's done
              return !(b > a);
          }
      
          public static bool operator <=(MyStruct a, MyStruct b)
          {
              return !(a > b);
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-11-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-01-19
        • 2014-03-05
        • 2011-02-03
        相关资源
        最近更新 更多