How can I define whether and how a comparison operator is applied to operands of my type?

有帮助吗?

解决方案

You implement the IComparable interface with the CompareTo method.

To use all of the operators, try this:

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();
    }
}

A good tutorial on this: http://winsharp93.wordpress.com/2009/06/28/implementing-icomparablet-iequatablet-and-the-equality-members/

Since you know the interfaces IComparable, IEquatable needs to be implemented you can tell if two instances of yourClass are comparable by using this (example):

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

其他提示

You can overload operators for your type, e.g.

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

Allows you to do this:

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

(in such a case, it would probably make sense to implement IComparable<MyComparable> also.

A simple class that implements IComparable, IComparable<T>, IEquatable<T> and overrides object.Equals(object), object.GetHashCode() and the various "standard" operators ==, !=, >, <, >=, <=.

Note the use of object.ReferenceEquals(object, object) so as not to trigger StackOverflowException. This because we are overloading the == and != operators and basing them on MyClass.Equals(MyClass), so MyClass.Equals(MyClass) clearly can't use them. A common error is, in fact, inside the

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

Booooom! Can't do that. Because the if (other == null) will recursively call the other.Equals((MyClass)null). What you could do is: if (((object)other) == null), because in C# operators can't be virtual, so here we are using the == of the object class.

InnerEquals and InnerCompareTo are present so the null check mustn't be done twice if the Equals(object) or CompareTo(object) are called.

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);
    }
}

And this one is the variant for struct types. A lot shorter, because nearly all the object.ReferenceEquals(object, object) are gone. Value types can't be null.

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);
    }
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top