目录
1、 IComparer < in T >
概述
IComparer<in T>
接口位于 System.Collections.Generic
命名空间中,用于定义比较两个对象的方法。
它通常用于自定义排序顺序。
接口定义
public interface IComparer<in T>
{
int Compare(T x, T y);
}
- T: 要比较的对象的类型。
IComparer<in T>
是逆变的,这意味着可以使用指定类型或其基类。
方法int Compare(T x, T y);
int Compare(T x, T y);
- 参数:
-x
: 第一个要比较的对象。
-y
: 第二个要比较的对象。 - 返回值:
- 小于 0:x
小于y
- 等于 0:x
等于y
- 大于 0:x
大于y
使用场景
IComparer<T>
接口通常与以下方法一起使用,根据不同的比较器实现排序和查找
List<T>.Sort
List<T>.BinarySearch
Array.Sort
Array.BinarySearch
相关类
Comparer<T>
:IComparer<T>
的默认实现。StringComparer
: 为String
类型实现IComparer<T>
接口。
注意事项
- 建议从
Comparer<T>
类派生,而不是直接实现IComparer<T>
接口,因为Comparer<T>
类提供了默认实现和其他有用的方法。
示例
以下示例展示了如何实现 IComparer<T>
接口来提供多种对标题进行排序的比较器。
- 按照标题序号进行排序
/// <summary>
/// 按照标题的序号进行排序
/// </summary>
/// <typeparam name="T"></typeparam>
public class TitleComparerByVersion<T> : Comparer<String>
{
/// <summary>
/// 比较两个字符串表示的序号大小。
/// </summary>
/// <param name="x">第一个版本号字符串</param>
/// <param name="y">第二个版本号字符串</param>
/// <returns>如果 x 大于 y,返回 1;如果 x 小于 y,返回 -1;相等则返回 0</returns>
public override int Compare(string? x, string? y)
{
if (string.IsNullOrEmpty(x) && string.IsNullOrEmpty(y))
{
return 0;
}
if (string.IsNullOrEmpty(x))
{
return -1;
}
if (string.IsNullOrEmpty(y))
{
return 1;
}
var xOrderStr = x.Split(' ')[0];
var yOrderStr = y.Split(' ')[0];
var xNumList = xOrderStr.Split('.');
var yNumList = yOrderStr.Split('.');
int length = Math.Min(xNumList.Length, yNumList.Length);
for (int i = 0; i < length; i++)
{
if (int.TryParse(xNumList[i], out int xNum) && int.TryParse(yNumList[i], out int yNum))
{
if (xNum != yNum)
{
return xNum.CompareTo(yNum);
}
}
else
{
throw new ArgumentException("版本号格式不正确");
}
}
return xNumList.Length.CompareTo(yNumList.Length);
}
}
[TestMethod]
public void DemoSort()
{
var titles = new List<string>
{
"1.1.3",
"1.1.1",
"1.1.2.1",
"1.1.2",
};
titles.Sort(_comparer);
var expected = new List<string>
{
"1.1.1",
"1.1.2",
"1.1.2.1",
"1.1.3",
};
Assert.IsTrue(expected.SequenceEqual(titles));
}
- 按照标题字母顺序进行排序
/// <summary>
/// 按照标题的字母顺序进行排序
/// </summary>
/// <typeparam name="T"></typeparam>
public class TitleCompareByAlphabet<T> : Comparer<string>
{
/// <summary>
/// 按照字母顺序进行排序
/// </summary>
/// <param name="x">第一个字符串</param>
/// <param name="y">第二个字符串</param>
/// <returns>比较结果</returns>
public override int Compare(string? x, string? y)
{
if (string.IsNullOrEmpty(x) && string.IsNullOrEmpty(y))
{
return 0;
}
if (string.IsNullOrEmpty(x))
{
return -1;
}
if (string.IsNullOrEmpty(y))
{
return 1;
}
int minLength = Math.Min(x.Length, y.Length);
for (int i = 0; i < minLength; i++)
{
int res = x[i].CompareTo(y[i]);
if (res != 0)
{
return res;
}
}
return x.Length.CompareTo(y.Length);
}
}
[TestMethod]
public void DemoSort()
{
var titles = new List<string> { "apple", "banana", "apricot", "apples", "appliqué" };
titles.Sort(_titleComparer);
var expected = new List<string> { "apple", "apples", "appliqué", "apricot", "banana" };
Assert.IsTrue(titles.SequenceEqual(expected));
}
参考
(1) IComparer 接口 (System.Collections.Generic) | Microsoft … https://learn.microsoft.com/zh-cn/dotnet/api/system.collections.generic.icomparer-1?view=net-8.0.
(2) IComparer Interface (System.Collections) | Microsoft Learn. https://learn.microsoft.com/en-us/dotnet/api/system.collections.icomparer?view=net-8.0.
(3) C# lambda表达式和IComparer-腾讯云开发者社区-腾讯云. https://cloud.tencent.com/developer/ask/sof/93306.
(4) C#泛型-泛型接口IComparer——比较器 - 流星落 - 博客园. https://www.cnblogs.com/tanding/archive/2012/06/29/2569392.html.
(5) C# IComparer 详解:如何实现和使用它-CSDN博客. https://blog.csdn.net/xiefeng240601/article/details/139375277.
2、 IComparable < in T >
概述
IComparable 接口定义了一种通用的比较方法CompareTo(T other)
.
用于创建特定类型的比较方法,以对其实例进行排序,实现此接口的类型可以与同一类型的其他对象进行比较。
接口定义
public interface IComparable<in T>
{
int CompareTo(T other);
}
方法CompareTo(T other)
- 描述:比较与当前对象同一类型的另一个对象,并返回一个整数,指示当前对象在排序顺序中的相对位置。
- 返回值:
- 小于零:当前对象小于other
。
- 等于零:当前对象等于other
。
- 大于零:当前对象大于other
。
使用场景
- 排序:实现
IComparable<in T>
接口的对象可以使用Array.Sort
、List<T>.Sort
等方法进行排序。 - 集合操作:在
SortedList<TKey, TValue>
、SortedDictionary<TKey, TValue>
等集合中使用时,可以根据键进行排序。
注意事项
- 一致性:实现
CompareTo
方法时,应确保比较操作的一致性和对称性。
一致性要求 CompareTo
方法在相同的对象之间进行比较时,始终返回相同的结果。这意味着,如果你在不同的时间或不同的环境中比较相同的两个对象,结果应该是一样的。
对称性要求 CompareTo
方法的返回值在两个对象之间是互为相反数的。也就是说,如果 x.CompareTo(y)
返回一个正数,那么 y.CompareTo(x)
应该返回一个负数,反之亦然。
- 相等性:当实现
IComparable<in T>
接口时,通常还需要重写Equals
和GetHashCode
方法,以确保对象的相等性逻辑与比较逻辑一致。
示例
以下实现了一个Apple类,它实现了 IComparable接口,用于比较两个Apple
对象的排序顺序。
在这个例子中,我们根据苹果的大小(Size)和糖分(Sugar)的比例进行比较,其中大小占70%权重,糖分占30%权重。
public class Apple : IComparable<Apple>
{
public string Variety { get; set; }
public int Size { get; set; }
public int Sugar { get; set; }
public string Guid { get; set; }
public int CompareTo(Apple other)
{
if (Size * 0.7 + Sugar * 0.3 > other.Size * 0.7 + other.Sugar * 0.3)
return 1;
else if (Size * 0.7 + Sugar * 0.3 < other.Size * 0.7 + other.Sugar * 0.3)
return -1;
else
return 0;
}
public override bool Equals(object? obj)
{
var orther = obj as Apple;
if (orther == null)
return false;
return Size * 0.7 + Sugar * 0.3 == orther.Size * 0.7 + orther.Sugar * 0.3;
}
public override int GetHashCode()
{
return HashCode.Combine(Guid);
}
}
[TestMethod]
public void SortDemo()
{
List<Apple> apples = new List<Apple>
{
new Apple { Size = 10, Sugar = 5 },
new Apple { Size = 8, Sugar = 6 },
new Apple { Size = 12, Sugar = 4 }
};
apples.Sort();
Assert.AreEqual(8, apples[0].Size);
Assert.AreEqual(10, apples[1].Size);
Assert.AreEqual(12, apples[2].Size);
}
3、 IComparer和IComparable区别
IComparer
和 IComparable
是 .NET 框架中用于定义对象比较逻辑的两种不同方式,它们在实现位置****、灵活性以及****用途等方面有所区别,具体分析如下:
- 实现位置:
IComparable<T>
在类内部实现。IComparer<T>
在类外部实现。
- 灵活性:
IComparable<T>
只能定义一种排序标准。IComparer<T>
可以定义多种排序标准。IComparable<T>
一旦类定义了比较逻辑,后续更改可能需要对类本身进行修改。而IComparer<T>
允许在不修改原有类的情况下添加新的比较逻辑,这有利于维护和扩展现有代码。因此,IComparer<T>
具有更高的版本兼容能力。
- 用途:
IComparable<T>
用于定义对象的自然排序。IComparer<T>
用于自定义排序逻辑。
针对上述分析,提出以下几点建议:
- 如果希望对象的比较逻辑能够灵活地在不同的上下文环境中变化,建议使用
IComparer<T>
。 - 如果您的应用中对象自然地知道如何比较自己,并且不需要多种比较策略,那么应实现
IComparable<T>
。 - 对于经常一起使用的一组类,您可能会发现同时实现这两个接口是最有用的,因为这样既提供了默认的比较逻辑,又保持了一定的灵活性。
在中文环境中,可能容易混淆这两个接口。其实只要理解其词性即可。IComparable
中的 able
代表能力,表示对象拥有此能力,而 Comparer
代表比较器,是名词,可以使用不同的比较器实现不同的比较结果。
参考资料
(1) c# - difference between IComparable and IComparer - Stack Overflow. https://stackoverflow.com/questions/5980780/difference-between-icomparable-and-icomparer.
(2) When to use IComparable Vs. IComparer - Stack Overflow. https://stackoverflow.com/questions/538096/when-to-use-icomparablet-vs-icomparert.
(3) IComparable, IComparer And IEquatable Interfaces In C#. https://www.c-sharpcorner.com/UploadFile/80ae1e/icomparable-icomparer-and-iequatable-interfaces-in-C-Sharp/.
(4) C# 中的IComparable和IComparer - HueiFeng - 博客园. https://www.cnblogs.com/yyfh/p/12129000.html.
(5) C# IComparable接口、IComparer接口以及排序应用(超详细 … https://blog.csdn.net/weixin_42185134/article/details/103149334.