【发布时间】:2013-08-14 13:09:27
【问题描述】:
如何定义比较运算符是否以及如何应用于我的类型的操作数?
【问题讨论】:
标签: c# comparison-operators icomparable
如何定义比较运算符是否以及如何应用于我的类型的操作数?
【问题讨论】:
标签: c# comparison-operators icomparable
您使用 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();
}
}
由于您知道 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 '<=' cannot be applied to operands of type 'System.IComparable' and 'System.IComparable' 为什么?
public class Base<T> : IEquatable<Base<T>>、T foo;、IComparable bar、if (foo is IComparable) if (((IComparable) foo) <= bar) doSomething();?
if (foo is IComparable) if (!((IComparable) foo).CompareTo(bar) > 0) doSomething();
你可以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<MyComparable> 也可能有意义。
【讨论】:
实现IComparable、IComparable<T>、IEquatable<T> 并覆盖object.Equals(object)、object.GetHashCode() 和各种“标准”运算符==、!=、>、< 的简单类,>=,<=。
注意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 类的==。
InnerEquals 和 InnerCompareTo 存在,因此如果调用了 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);
}
}
【讨论】: