我最近开始学习Linq。我遇到了一些内置方法,如Min()和Max()。对于int[],这两个方法的工作很好。但是当涉及到string[]时,我很好奇它是如何工作的。我已经尝试了一些代码
string[] cars = { "Volvo", "BMW", "Ford", "Mazda" };
Console.WriteLine(cars.Max());
Console.WriteLine(cars.Min());
输出如下:
**Volvo for Max()
BMW for Min()**
你能解释一下它是如何工作的,它是按字母顺序取第一个字母,还是有任何机制,它是使用如基于ASCII值等?
所有实现IComparable或IComparable接口的类型都可以通过使用每个类型实现的CompareTo方法进行比较。所有原语类型都实现IComparable<T>
,包括char
和string
。LINQ的Min(IEnumerable)
和Max(IEnumerable)
使用这个实现来查找可枚举对象中的最小值或最大值。
字符串比较
比较字符串要比比较整数有趣一些。字符串通常按字典顺序(按字典顺序)进行比较,但是…他的字典吗?不同的语言有不同的排序规则,有时两个字母被认为是一个字母。即使丹麦人也忘记了AA
在丹麦语中相当于Å
。
用于比较字符串的字典由CultureInfo类提供。默认情况下,使用当前线程的区域性,它通常与最终用户的区域性(在桌面应用程序中)或服务器应用程序中的系统区域设置相匹配。例如,在丹麦文化中,AA
和aa
的对待方式是不同的——我认为其中一个是在相同大小写的其他字母之后排序的,而另一个不是,但不要问我是哪个。
InvariantCulture指定敏感区域性中的区域设置,该区域设置可用于在每个区域设置中以相同的方式处理字符串。它主要使用合理的设置(例如小数点为.
),除了日期,它使用美国格式而不是每个人都期望的ISO8601 (YYYY-MM-DD)格式。
自定义比较
可以通过将实现IComparer
的类传递给受顺序影响的LINQ方法来指定不同的比较方法。Min(IEnumerable, iccompparer)就是一个例子。
StringComparer类包含一些预定义的比较器:
- CurrentCulture是默认的
- CurrentCultureIgnoreCase使用当前区域性,但忽略大小写,因此
A
等于a
。这是非常有用的,例如在字典中。 - InvariantCulture和
InvariantCultureIgnoreCase
使用不变区域性排序 最后,Ordinal和
OrdinalIgnoreCase
不使用字典,而是比较字符的Unicode值。如果你不关心语言环境规则,这是最快的选择.Max()
使用Compare(String, String)
它比较两个指定的String对象,并返回一个整数,指示它们在排序顺序中的相对位置。
.Max()
字符串比较的源代码
public static TSource Max<TSource>(this IEnumerable<TSource> source) {
if (source == null) throw Error.ArgumentNull("source");
Comparer<TSource> comparer = Comparer<TSource>.Default;
TSource value = default(TSource);
if (value == null) {
foreach (TSource x in source) {
if (x != null && (value == null || comparer.Compare(x, value) > 0))
value = x;
}
return value;
}
else {
bool hasValue = false;
foreach (TSource x in source) {
if (hasValue) {
if (comparer.Compare(x, value) > 0) //Compare strings
value = x;
}
else {
value = x;
hasValue = true;
}
}
if (hasValue) return value;
throw Error.NoElements();
}
}
比较(字符串,字符串)https://learn.microsoft.com/en us/dotnet/api/system.string.compare?view=net - 6.0 # system-string-compare (system-string-system-string)
字符串按字母顺序进行比较,这转化为以下逻辑:
- 哪个物品的首字符更高
- 哪个物品的第二个字符更高
- 哪个物品有更高的第三个字符
- …
从第一个不同的字符开始检测差异。因此,当比较string1
和string2
时,如果在它们的第一个不同字符处(从左到右),string1
在该位置的值大于string2
,则string1
大于string2
。如果string2
=string1
+string3
,则string1
与string2
的第一个差值超出了string1
的末尾,此时比较得出string2
大于string1
。
如果您对这个比较不满意,那么您可以指定您打算使用哪个比较器,参见这里:https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.max?view=net-6.0#system-linq-enumerable-max-1(system-collections-generic-ienumerable((-0))-system-collections-generic-icomparer((-0)))
基本上在这种情况下,你需要实现一个IComparer
并将其作为第二个参数传递,如
cars.Max(c => c, yourcomparer)
对于字符串列表,Min()或Max()分别跟随第一个和最后一个单词或字母,但对于整数,Min()或Max()遵循从列表中查找最小和最大数字的确切现象。请检查此图像及其输出