Linq对泛型列表进行OrderBy排序返回的不完全是按字母顺序排列的列表

7

我正在尝试使用对象的名称属性对通用对象列表进行排序。 我正在使用LINQ,以下表达式不太起作用:

var query = possibleWords.OrderBy(x => x.Name.ToLower()).ToList();
foreach (Word word in query) //possibleWords.OrderBy(word => word.Name))
   {
            listWords.Items.Add(word.Name);
   }

如果我理解正确,“query”现在应该包含一个有序列表,如果要将某个项目添加到名为listWords的列表框中。
然而输出结果是这样的:http://screencast.com/t/s1CkkWfXD4(抱歉只能提供URL链接,因为SO似乎锁定了我的帐户,并且我不能在新账户中发布图片)。
列表框几乎按字母顺序排列,但有些例外。由于某种原因,“aa”和“aaaa”排在最后。可能的原因是什么?如何解决?
提前感谢您的回答。
根据请求进一步解释:当在Visual Studio中输入并执行此代码时:
        List<Word> words = new List<Word>();

        words.Add(new Word("a"));
        words.Add(new Word("Calculator"));
        words.Add(new Word("aaa"));
        words.Add(new Word("Projects"));
        words.Add(new Word("aa"));
        words.Add(new Word("bb"));
        words.Add(new Word("c"));

        IEnumerable<Word> query = words.OrderBy(x => x.Name.ToLower()).ToList();

        foreach (Word word in query)
        {
            Console.WriteLine(word.Name);
        }

给我以下输出结果:
a
bb
c
Calculator
ccc
Projects
aa
aaa

这并没有正确排序:第一个"a"是对的,但随后的"aa"和"aaa"条目被放到了列表底部。我对字符集和编码不太了解,所以可能我在这里犯了一个初学者的错误。但如果是这样的话,我不知道是什么错误,并且我会有点困惑,为什么第一个"a"可以正确排序,但第二个和第三个"aa"和"aaa"不能!更进一步的解释-单词类别。
[Serializable()]
public class Word
{
    [System.Xml.Serialization.XmlAttribute("Name")]
    public string Name { get; set; }

    public Word(string name)
    {
        Name = name;
    }

    public Word() { } //Parameter less constructor neccessary for serialization

}

原因和解决方法

像@Douglas建议的那样,将StringComparer.InvariantCultureIgnoreCase比较器提供给OrderBy方法可以解决问题。

进一步研究发现,使用丹麦文化(da-DK)时,FindAll和OrderBy方法(可能还有其他方法)存在问题。可能会有其他失败的方法或文化,但是da-DK文化和FindAll + OrderBy方法绝对不能正常工作。

OrderBy方法存在如本主题中描述的问题(错误排序)。 FindAll方法有一个类似的、非常奇怪的问题:假设我们有一个条目列表:a、aa、aaa和aaaa。当使用FindAll(x => x.StartsWith("a"))时,它只会返回"a",而不是aa、aaa和aaaa。如果使用StartsWith("aa"),它将正确地找到aa,以及aaa和aaaa。当使用StartWith("aaa")时,它又不会找到aaaa,只有aaa!这似乎是框架中的一个错误。


请确保您不会在后面添加任何项目。即验证query.Count()是否等于列表中单词的数量。同时,请确保只使用英文字符。 - Sergey Berezovskiy
1
你能否提供一个简单的示例来展示你的情况,而不是发布一个图片链接? - L.B
1
我已经尝试了相同的操作和相同的词汇,它已经正确排序。 - S3ddi9
1
@user1830478 请展示你的 Word 类。 - Sergey Berezovskiy
1
@user1830478,你还没有发布与Word类相关的代码,这才是你真正的问题所在......只需将你的代码中的Word替换为string,你就会发现它可以工作。 - L.B
显示剩余8条评论
3个回答

6

你能尝试进行替换吗:

IEnumerable<Word> query = words.OrderBy(x => x.Name.ToLower()).ToList();

…with:

IEnumerable<Word> query = words.OrderBy(x => x.Name, 
    StringComparer.InvariantCultureIgnoreCase);

有可能这是一个奇怪的文化问题,但概率非常小。


1
+1. 我认为这是目前唯一合理的解释 - OP对文化比较的理解偏差太大了... - Alexei Levenkov
那真的起作用了。我不明白为什么,除了像你说的道格拉斯,可能是一些奇怪的文化问题。我没有使用任何特殊的奇怪文化,所以这非常奇怪。但它起作用了,所以我想我现在可以去睡觉了,并尝试重新赢得妻子的好感。非常感谢! - Morten Kirsbo
е°қиҜ•жЈҖжҹҘ CultureInfo.CurrentCulture е’Ң CultureInfo.CurrentUICulture зҡ„еҖјгҖӮдҪҶжҳҜе…ҲзҘқдҪ жҷҡе®ү :-) - Douglas
谢谢,我会研究一下为什么丹麦文化da-DK会这样。虽然很奇怪,但明天一定要解决。再次感谢,祝你晚安(或白天愉快) :-) - Morten Kirsbo

5
以下代码输出预期结果:
class Word
{
    public Word(string str)
    {
        Name = str;
    }

    public string Name { get; private set; }
}

public static void Main(string[] args)
{
    List<Word> words = new List<Word>();

    words.Add(new Word("a"));
    words.Add(new Word("Calculator"));
    words.Add(new Word("aaa"));
    words.Add(new Word("Projects"));
    words.Add(new Word("aa"));
    words.Add(new Word("bb"));
    words.Add(new Word("c"));

    IEnumerable<Word> query = words.OrderBy(x => x.Name.ToLower()).ToList();

    foreach (Word word in query)
    {
        Console.WriteLine(word.Name);
    }
}

输出:

a
aa
aaa
bb
c
Calculator
Projects

更新: 好的,谜团解开了(有点)。如果在您的代码之前执行以下操作:

var cultureInfo = new CultureInfo("da-DK");
Thread.CurrentThread.CurrentCulture = cultureInfo;
Thread.CurrentThread.CurrentUICulture = cultureInfo;

您得到了“错误”的输出:
a
bb
c
Calculator
Projects
aa
aaa

显然,丹麦的词汇比较规则不同。我在网上找到了一段解释(https://dev59.com/ZG855IYBdhLWcg3w75Iv
请注意,这与所使用的语言环境紧密相关。例如,在丹麦,有一个字母“å”,曾经拼写为“aa”,并且与两个连续的字母“a”非常不同。因此,丹麦的排序规则将两个相邻的字母“a”视为与“å”相同,这意味着它排在z之后。这也意味着丹麦词典的排序方式与英语或瑞典的排序方式不同。

这很奇怪。我已经查看了类定义,除了将字符串参数传递给单词类中的公共字段外,在构造函数中我没有做任何事情。 - Morten Kirsbo
我刚刚尝试使用普通字符串而不是Word对象 - 输出结果仍然相同。但是似乎有几个人尝试了相同的代码,得到了正确的结果,所以我感到困惑! - Morten Kirsbo
我也尝试着逐字复制你的示例,但排序结果也不正确。非常奇怪。 - Morten Kirsbo
以下代码会输出什么内容?Console.WriteLine(System.Globalization.CultureInfo.CurrentCulture); - Grozz
输出结果为 "da-DK"(丹麦文化)。 - Morten Kirsbo
1
这是一个非常好的发现@Grozz :-) 我以为这是一个错误,但实际上它似乎是“按照意图工作”。我认为这是一个不好的规则,因为“aa”拼写在1948年就已经从丹麦语中删除了,但至少这个决定背后有一些逻辑。谢谢! - Morten Kirsbo

2

很可能你最后的 "a" 是一些不同的(非ASCII)字符。检查字符代码 (int)("a"[0]),看看它是否与英语中的 "a" 相同。

如果是这种情况,排序没有问题-没有什么需要修复的(除了可能更好地理解你的数据)。


所有条目都是通过代码添加到文本中的,例如:ListObj.Add("a"),ListObj.Add("aaaa"),ListObj.Add("Calculator"),ListObj.Add("bbb") 等等。 - Morten Kirsbo
1
@user1830478,我不确定你在评论中想要表达什么。你是在暗示C#源代码只能包含ASCII字符(显然是错误的)吗? - Alexei Levenkov
尝试通过控制台或消息框打印以下内容:MessageBox.Show(string.Format("{0}", (int)'a'));,并查看输出是否为 97,请用复制粘贴方式。 - S3ddi9
Seddik:它输出97。 Levenkov:我想表达的是,第一个被正确排序的“a”,与“aa”和“aaaa”字符串中使用的字符相同。 - Morten Kirsbo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接