有时候我们需要对一个对象数组进行排序,像string这种默认实现了IComparable<T>接口的类型,我们可以直接调用Sort()方法排序。
可是如果元素类型没有实现IComparable<T>,或者默认的比较逻辑不符合要求,又该怎么办呢?
为了指定非默认的排序顺序,可以调用List<T>.Sort()的重载版本,它获取一个IComparer<T>作为实参。
首先我们需要了解一下IComparable<T>和IComparer<T>的区别:
IComparable<T>:“我知道如何将我自己和我的类型的另一个实例进行比较”;
IComparer<T>:“我知道如何比较给定类型的两个实例”;
很明显,IComparable<T>由需要排序的对象继承并实现,IComparer<T>需要另建一个类继承并实现;
来看下面这个例子:
首先新建一个需要排序的类Name:
class Name
{
public string FirstName { get; private set; }
public string LastName { get; private set; }
public Name(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public override string ToString()
{
return String.Format($"{FirstName} {LastName}");
}
}
然后新建一个比较类,继承并实现IComparer<T>接口:
class NameComparison : IComparer<Name>
{
public int Compare(Name x, Name y)
{
if (Object.ReferenceEquals(x, y))
return 0;
if (x == null)
return 1;
if (y == null)
return -1;
int result = StringCompare(x.FirstName, y.FirstName);
if (result == 0)
{
result = StringCompare(x.LastName, y.LastName);
}
return result;
}
public static int StringCompare(string x, string y)
{
if (Object.ReferenceEquals(x, y))
return 0;
if (x == null)
return 1;
if (y == null)
return -1;
return x.CompareTo(y);
}
}
接下来在Main方法中调用:
class Program
{
static void Main(string[] args)
{
List<Name> nameList = new List<Name>()
{
new Name("Michelle", "Green"),
new Name("Susan", "Miller"),
new Name("Susan",""),
new Name("","Smith"),
new Name("Keisey","Smith")
};
nameList.Sort(new NameComparison());
foreach (Name item in nameList)
{
Console.WriteLine(item);
}
}
}
这样就能得到输出结果:
注意:实现IComparable<T>和IComparer<T>时必须生成一个全序。
CompareTo的实现必须为任何可能的数据排列组合提供完全一致的排序结果。
例如上述代码中考虑到了实参为null的情况。不能“任何一个元素为null就返回0,”,否则两个元素可能等于null,但相互不等,程序可能会崩溃!