复杂对象的自然排序抽象类

时间:2014-11-26 19:04:46

标签: c# generics abstract-class

所有

我正在创建一个类,通过类中指定的字符串属性对对象列表进行排序。 我这样做是通过继承IComparer和List提供的Sort()方法。

我的comparer类是抽象的,因此可以在任何对象类型上调用它,只要有一个要比较的字符串属性,当然。

我遇到的问题是如何指定该类正在处理的泛型类的字符串属性类型。在下面的代码中,我有一个名为" MyObjectsStringProperty"的占位符。这就是我正在努力的方向。如何在这些地方提供有效的语法。

由于

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;


namespace NaturalSorterDemo
{
    public abstract class NaturalComparerObject<T> : Comparer<T>, IDisposable
    {
       private Dictionary<string, string[]> _table;

       protected NaturalComparerObject()
       {
            _table = new Dictionary<string, string[]>();
       }
       public void Dispose()
       {
            _table.Clear();
            _table = null;
       }



       public override int Compare(T x, T y)
       {
           if (x.MyObjectsStringProperty.ToLower() == y.MyObjectsStringProperty.ToLower())
           {
               return 0;
           }
           string[] x1, y1;
           if (!_table.TryGetValue(x.MyObjectsStringProperty.ToLower(), out x1))
           {
               x1 = Regex.Split(x.MyObjectsStringProperty.Replace(" ", ""), "([0-9]+)");
               _table.Add(x.MyObjectsStringProperty.ToLower(), x1);
           }
           if (!_table.TryGetValue(y.MyObjectsStringProperty.ToLower(), out y1))
           {
               y1 = Regex.Split(y.MyObjectsStringProperty.Replace(" ", ""), "([0-9]+)");
               _table.Add(y.MyObjectsStringProperty.ToLower()
                   , y1);
           }

           for (int i = 0; i < x1.Length && i < y1.Length; i++)
           {
               if (x1[i].ToLower() != y1[i].ToLower())
               {
                   return PartCompare(x1[i], y1[i]);
               }
           }
           if (y1.Length > x1.Length)
           {
               return 1;
           }
           else if (x1.Length > y1.Length)
           {
               return -1;
           }
           else
           {
               return 0;
           }
       }

       private static int PartCompare(string left, string right)
       {
           int x, y;
           left = left.ToLower();
           right = right.ToLower();

           if (!int.TryParse(left, out x))
           {
               return left.CompareTo(right);
           }

           if (!int.TryParse(right, out y))
           {
               return left.CompareTo(right);
           }

           return x.CompareTo(y);
       }
    }
}

1 个答案:

答案 0 :(得分:1)

首先,代码示例中存在一些根本性的错误:

  1. 该课程abstract没有明显原因。如果您在课程中没有abstract个成员,那么请不要自己创建课程abstract
  2. 该类实现IDisposable没有明显的理由。该接口主要用于处理非托管资源。这里不需要它,甚至不需要暴露任何机制来清除私人字典。当类本身被收集时,字典将被收集,并且可能直到那个字典仍然有用。
  3. 使用ToLower()作为实现不区分大小写的比较的方法并不完全安全;它依赖于文化,但不适用于字符串比较。至少,您应该使用ToLowerInvariant()。但即使这对你来说可靠,它仍然会导致创建大量新的字符串实例。正确的方法是使用指定不区分大小写的比较的StringComparison值之一或适当的StringComparer实例(适合字典使用)执行所有字符串比较。 Best Practices for Using Strings in the .NET FrameworkDoes Your Code Pass The Turkey Test?是一些供您查看的相关资源。
  4. 其次,我觉得你在这里确实不需要通用类型。正如Servy在评论中提到的那样,最好限制这里的功能并根据需要进行组合。您可以为调用者提供一种指定选择器的方法,但鉴于.NET已经提供了易于编写的IComparer<T>功能,这在这里似乎是多余的。

    所以如果你让你的类型看起来像这样:

    public abstract class NaturalComparerObject : Comparer<string>
    {
        private Dictionary<string, string[]> _table;
    
        protected NaturalComparerObject()
        {
            _table = new Dictionary<string, string[]>();
        }
    
        public override int Compare(string x, string y)
        {
            // implementation omitted for brevity
        }
    }
    

    然后你可以使用这样的东西:

    NaturalComparerObject stringComparer = new NaturalComparerObject();
    
    myList.Sort((x, y) =>
        stringComparer.Compare(x.MyObjectsStringProperty, y.MyObjectsStringProperty));
    

    最后,我建议您可能不需要字典,这意味着您的比较实现可能只是一个静态方法。看来,字典在那里缓存删除空格和分割数字的结果,这些操作当然是昂贵的,特别是在对象创建方面。但是只要付出一点努力,你就可以编写一个简单地迭代字符串字符的比较,而不必创建首先激发字典使用的中间对象。

    由于您没有缓存比较结果,因此字典唯一可以帮助您避免的是对象创建的开销。您仍然需要比较原始字符串的各个部分,因此执行相同但不创建中间对象的实现将执行大致相同的操作,但没有额外的内存开销(即使使用缓存,该字典并且包含在其中的对象对于具有大量字符串的任何场景来说都是昂贵的。)