如何实现可比接口?

我正在用类的实例填充数组:

BankAccount[] a;
. . .


a = new BankAccount[]
{
new BankAccount("George Smith", 500m),
new BankAccount("Sid Zimmerman", 300m)
};

一旦我填充了这个数组,我将按照余额对它进行排序。为此,我希望能够使用 IComparable检查每个元素是否可以排序。
我需要使用接口来完成这项工作:

public interface IComparable
{
decimal CompareTo(BankAccount obj);
}

但我不确定这是否是正确的解决方案,有什么建议吗?

101790 次浏览

There is already IComparable<T>, but you should ideally support both IComparable<T> and IComparable. Using the inbuilt Comparer<T>.Default is generally an easier option. Array.Sort, for example, will accept such a comparer.

You should not define IComparable yourself. It is already defined. Rather, you need to implement IComparable on your BankAccount class.

Where you defined the class BankAccount, make sure it implements the IComparable interface. Then write BankAccount.CompareTo to compare the balance amounts of the two objects.

public class BankAccount : IComparable<BankAccount>
{
[...]


public int CompareTo(BankAccount that)
{
if (this.Balance <  that.Balance) return -1;
if (this.Balance == that.Balance) return 0;
return 1;
}
}

Edit to show Jeffrey L Whitledge's solution from comments:

public class BankAccount : IComparable<BankAccount>
{
[...]


public int CompareTo(BankAccount that)
{
return this.Balance.CompareTo(that.Balance);
}
}

IComparable already exists in .NET with this definition of CompareTo

int CompareTo(Object obj)

You are not supposed to create the interface -- you are supposed to implement it.

public class BankAccount : IComparable {


int CompareTo(Object obj) {
// return Less than zero if this object
// is less than the object specified by the CompareTo method.


// return Zero if this object is equal to the object
// specified by the CompareTo method.


// return Greater than zero if this object is greater than
// the object specified by the CompareTo method.
}
}

An alternative is to use LINQ and skip implementing IComparable altogether:

BankAccount[] sorted = a.OrderBy(ba => ba.Balance).ToArray();

Do you want to destructively sort the array? That is, do you want to actually change the order of the items in the array? Or do you just want a list of the items in a particular order, without destroying the original order?

I would suggest that it is almost always better to do the latter. Consider using LINQ for a non-destructive ordering. (And consider using a more meaningful variable name than "a".)

BankAccount[] bankAccounts = { whatever };
var sortedByBalance = from bankAccount in bankAccounts
orderby bankAccount.Balance
select bankAccount;
Display(sortedByBalance);

If you only need to sort these BankAccounts, use LINQ like following

BankAccount[] a = new BankAccount[]
{
new BankAccount("George Smith", 500m),
new BankAccount("Sid Zimmerman", 300m)
};


a = a.OrderBy(bank => bank.Balance).ToArray();

If you need to compare multiple fields, you can get some help from the compiler by using the new tuple syntax:

public int CompareTo(BankAccount other) =>
(Name, Balance).CompareTo(
(other.Name, other.Balance));

This scales to any number of properties, and it will compare them one-by-one as you would expect, saving you from having to implement many if-statements.

Note that you can use this tuple syntax to implement other members as well, for example GetHashCode. Just construct the tuple and call GetHashCode on it.

This is an example to the multiple fields solution provided by @Daniel Lidström by using tuple:

   public static void Main1()
{
BankAccount[] accounts = new BankAccount[]
{
new BankAccount()
{
Name = "Jack", Balance =150.08M
}, new BankAccount()
{
Name = "James",Balance =70.45M
}, new BankAccount()
{
Name = "Mary",Balance =200.01M
}, new BankAccount()
{
Name = "John",Balance =200.01M
}};
Array.Sort(accounts);
Array.ForEach(accounts, x => Console.WriteLine($"{x.Name} {x.Balance}"));
}


}
public class BankAccount : IComparable<BankAccount>
{
public string Name { get; set; }
        

public int Balance { get; set; }


public int CompareTo(BankAccount other) =>
(Balance,Name).CompareTo(
(other.Balance,other.Name ));


}




Try it