代码改变世界

如何在 C# 中自定义 Comparer,以实现按中文拼音(a-z)来排序

2019-09-05 11:35  音乐让我说  阅读(960)  评论(0)    收藏  举报

1. 为何要自定义 Comparer

a. 先看如下代码

    class Program
    {
        public static void Main(string[] args)
        {
            List<string> words = new List<string>() { "", "b", "", "a", "", "k", "" };
            words.Sort();
            PrintResult(words);
            Console.ReadLine();
        }

        private static void PrintResult(IEnumerable<string> words)
        {
            if (words == null)
            {
                return;
            }
            int i = 0;
            foreach (string item in words)
            {
                if (i > 0)
                {
                    Console.Write(", ");
                }
                Console.Write(item);
                i++;
            }
        }
    }

 

 

b. 运行结果

结论:和我们预想的不一致!

 

2. 自定义 Comparer

CultureStringComparer.cs

using System;
using System.Globalization;

public class CultureStringComparer : StringComparer
{
    private CompareInfo _compareInfo;
    private CompareOptions _options;
    private bool _ignoreCase;

    public CultureStringComparer(CompareInfo compareInfo, CompareOptions options)
    {
        _compareInfo = compareInfo;
        _options = options;
        _ignoreCase = (options & CompareOptions.IgnoreCase) == CompareOptions.IgnoreCase ||
            (options & CompareOptions.OrdinalIgnoreCase) == CompareOptions.OrdinalIgnoreCase;
    }

    public override bool Equals(object obj)
    {
        if (!(obj is CultureStringComparer comparer))
            return false;

        if (_ignoreCase != comparer._ignoreCase)
            return false;

        return _compareInfo.Equals(comparer._compareInfo) && _options == comparer._options;
    }

    public override int GetHashCode()
    {
        int code = _compareInfo.GetHashCode();
        if (!_ignoreCase)
            return code;

        return ~code;
    }

    public override bool Equals(string x, string y) => 
    (
        string.Equals(x, y, StringComparison.Ordinal) 
        || (x != null && y != null) && _compareInfo.Compare(x, y, _options) == 0
    );

    public override int Compare(string x, string y)
    {
        if (string.Equals(x, y, StringComparison.Ordinal))
            return 0;

        if (x == null)
            return -1;

        if (y == null)
            return 1;

        return _compareInfo.Compare(x, y, _options);
    }

    public override int GetHashCode(string obj)
    {
        if (obj == null)
            throw new ArgumentNullException(nameof(obj));

        return _compareInfo.GetHashCode(obj, _options);
    }
}

 

测试示例

class Program
{
    public static void Main(string[] args)
    {
        CultureStringComparer cultureStringComparer = new CultureStringComparer(
            CompareInfo.GetCompareInfo("zh-cn"), 
            CompareOptions.IgnoreCase);
        List<string> words = new List<string>()
        {
            "", "b", "", "a", "", "k", ""
        };
        words.Sort(cultureStringComparer);

        PrintResult(words);
        Console.ReadLine();
    }

    private static void PrintResult(IEnumerable<string> words)
    {
        if (words == null)
        {
            return;
        }
        int i = 0;
        foreach (string item in words)
        {
            if (i > 0)
            {
                Console.Write(", ");
            }
            Console.Write(item);
            i++;
        }
    }
}

 

运行结果