通用IComparer,用于对不同属性中的不同对象进行排序

时间:2013-11-19 15:00:49

标签: c# arrays sorting icomparable icomparer

我正在尝试使用IComparer对对象数组进行排序。

我编写了代码,但它只适用于特定对象。 e.g:

这个班级

public class Cars
{
    public string Name { get; set; }
    public string Manufacturer { get; set; }
    public int Year { get; set; }

    public Cars(string name, string manufacturer, int year)
    {
        Name = name;
        Manufacturer = manufacturer;
        Year = year;
    }
}

我的代码如下:

class MySort 
{
    public class SortByYears : IComparer
    {
        int IComparer.Compare(Object x, Object y)
        {
            Cars X = (Cars)x, Y = (Cars)y;                
            return (X.Year.CompareTo(Y.Year));
        }
    }

    public class SortByName : IComparer
    {
        int IComparer.Compare(Object x, object y)
        {
            Cars X = (Cars)x, Y = (Cars)y;
            return (X.Name.CompareTo(Y.Name));
        }
    }

    public class SortByManyfacturer : IComparer
    {
        int IComparer.Compare(object x, object y)
        {
            Cars X = (Cars)x, Y = (Cars)y;
            return (X.Manufacturer.CompareTo(Y.Manufacturer));
        }
    }
}   

但是如果我添加另一个具有不同属性的类,它将毫无用处。

那么是否有机会修改此代码以使其适用于具有不同属性的对象?

6 个答案:

答案 0 :(得分:3)

class SortComparer<T> : IComparer<T>
{
   private PropertyDescriptor PropDesc = null;
   private ListSortDirection Direction =
      ListSortDirection.Ascending;

   public SortComparer(object item,string property,ListSortDirection direction)
   {
       PropDesc = TypeDescriptor.GetProperties(item)[property];
       Direction = direction;
   }

   int IComparer<T>.Compare(T x, T y)
   {    
      object xValue = PropDesc.GetValue(x);
      object yValue = PropDesc.GetValue(y);
      return CompareValues(xValue, yValue, Direction);
   }

   private int CompareValues(object xValue, object yValue,ListSortDirection direction)
   {

      int retValue = 0;
      if (xValue is IComparable) // Can ask the x value
      {
         retValue = ((IComparable)xValue).CompareTo(yValue);
      }
      else if (yValue is IComparable) //Can ask the y value
      {
         retValue = ((IComparable)yValue).CompareTo(xValue);
      }
      // not comparable, compare String representations
      else if (!xValue.Equals(yValue))
      {
         retValue = xValue.ToString().CompareTo(yValue.ToString());
      }
      if (direction == ListSortDirection.Ascending)
      {
         return retValue;
      }
      else
      {
         return retValue * -1;
      }
   }
}

致电代码:

假设名为lst:

的列表
lst.Sort(new SortComparer<Cars>(lst[0],"YourPropertyName",ListSortDirection.Ascending));

答案 1 :(得分:2)

使用接口并使用通用IComparer Interface代替IComparer

public interface IObjectWithNameProperty
{
    string Name {get; set;}
}

public class MyNameComparer : IComparer<IObjectWithNameProperty>
{
    public int Compare(IObjectWithNameProperty x, IObjectWithNameProperty y)
    {
        ...
    }
}

public class Car: IObjectWithNameProperty
{
     public string Name  {get;set;}
     ...
}
public class Dog: IObjectWithNameProperty
{
     public string Name  {get;set;}
     ...
}

答案 2 :(得分:2)

您可以利用Create Comparer<T>方法获取Comparison代表并返回Comparer<T>

var carnameComparer = Comparer<Cars>.Create((x, y) => x.Year.CompareTo(y.Year));
var carManufacturerComparer = Comparer<Cars>.Create((x, y) => x.Manufacturer.CompareTo(y.Manufacturer));

和其他类型

var carsComparer = Comparer<SomeType>.Create((x, y) => x.SomeProperty.CompareTo(y.SomeProperty));

如果您在.Net4.5之前,则可以使用以下CreateComparer方法。

private static IComparer<T> CreateComparer<T>(Comparison<T> comparison)
{
    return new ComparisonComparer<T>(comparison);
}

public class ComparisonComparer<T> : IComparer<T>
{
    private Comparison<T> comparison;
    public ComparisonComparer(Comparison<T> comparison)
    {
        if (comparison == null)
        {
            throw new ArgumentNullException("comparison");
        }
        this.comparison = comparison;
    }

    public int Compare(T x, T y)
    {
        return comparison(x, y);
    }
}

答案 3 :(得分:0)

最干净的方式是定义两个对象实现的接口,然后在比较中使用它。否则你将会有一堆案例陈述,具体取决于 可能的对象组合:

public class SortByYears : IComparer
{
    int IComparer.Compare(Object x, Object y)
    {
        if(x is Cars)
        {
            Cars X = (Cars)x
            if(y is Cars)
            {
                Y = (OtherCars)y;                
                return (X.Year.CompareTo(Y.Year));

            if(y is OtherCars)
            {
                Y = (OtherCars)y;                
                return (X.Year.CompareTo(Y.Year));
            }
        }
        if(x is OtherCars)
        {
             ... repeat upper block
        }
    }
}

答案 4 :(得分:0)

另一种方法是使用泛型IComparer interface和lambda表达式。

class CarComparer<T> : IComparer<Car> where T : IComparable<T>
{
    private readonly Func<Car, T> _sortExpression;

    public CarComparer(Func<Car, T> sortExpression)
    {
        _sortExpression = sortExpression;
    }

    public int Compare(Car x, Car y)
    {
        return _sortExpression(x).CompareTo(_sortExpression(y));
    }
}

此类比较构造函数中传入的Car的属性。

// Sort the cars by name
var nameCarComparer = new CarComparer<string>(car => car.Name);
Array.Sort(myArray, nameCarComparer);

答案 5 :(得分:0)

这是基于特里博兹齐奥的另一种观点。

public class PropertyComparer<T> : IComparer<T> where T : new()
{
    private PropertyDescriptor PropDesc = null;
    private ListSortDirection Direction = ListSortDirection.Ascending;

    public PropertyComparer(string property, ListSortDirection direction)
    {
        T item = new T();
        PropDesc = TypeDescriptor.GetProperties(item)[property];
        Direction = direction;


        Type interfaceType = PropDesc.PropertyType.GetInterface("IComparable");

        if (interfaceType == null && PropDesc.PropertyType.IsValueType)
        {
            Type underlyingType = Nullable.GetUnderlyingType(PropDesc.PropertyType);

            if (underlyingType != null)
            {
                interfaceType = underlyingType.GetInterface("IComparable");
            }
        }

        if (interfaceType == null)
        {
            throw new NotSupportedException("Cannot sort by " + PropDesc.Name +
                ". This" + PropDesc.PropertyType.ToString() +
                " does not implement IComparable");
        }
    }

    int IComparer<T>.Compare(T x, T y)
    {
        object xValue = PropDesc.GetValue(x);
        object yValue = PropDesc.GetValue(y);
        IComparable comparer = (IComparable)xValue;
        if (Direction == ListSortDirection.Ascending)
        {
            return comparer.CompareTo(yValue);
        }
        else
        {
            return -1 * comparer.CompareTo(yValue);
        }
    }
}