2013-11-19 3 views
1

Я пытаюсь сортировать массив объектов с помощью IComparer.Общий IComparer для сортировки разных объектов в разных свойствах

Я написал код, но он работает только с конкретным объектом. например .:

для этого класса

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)); 
     } 
    } 
} 

Но если добавить еще один класс с различными свойствами, это будет бесполезно.

Итак, есть ли шанс изменить этот код, чтобы он работал для объектов с различными свойствами?

+1

Я не уверен, что я понимаю ваш вопрос. Таким образом, вы можете использовать SortByName также для класса Cat, но не хотите его снова использовать для Cat, правильно? Вам нужен базовый класс для автомобилей и Cat, который содержит имя свойства, а затем измените Comparer на: –

+0

общественного класса SortByName: IComparer { INT IComparer.Compare (Object х, объект у) { Base X = (Base) x, Y = (базовый) y; return (X.Name.CompareTo (Y.Name)); } } –

+0

, то вы можете сравнить 2 кота по имени, 2 автомобиля по имени и кошку и автомобиль по имени. –

ответ

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;} 
    ... 
} 
+0

Я думаю, что это то, что мне нужно. Спасибо и извините, если я вас смутил. – Mikho

+0

@Mikho :) проблем нет! – giammin

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 
     } 
    } 
} 
+0

Вам лучше просто создавать N разных сопоставлений, чем проверять тип, похожий на них. Он добавляет очень мало дополнительной работы и добавляет потенциал для безопасности типов. – Servy

+0

Я думаю, что этот код просто показывает, что * не * делать, не так ли? –

+0

@ JuliánUrbano точно. –

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); 
    } 
} 
+0

только в рамках 4.5 или более поздней версии –

+0

@ JuliánUrbano Обновлен мой ответ –

2
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)); 
+0

, если вам нужно использовать 'is', а затем лучше использовать' as' – giammin

+0

Я предполагаю, что слишком много неизвестного синтаксиса. Но я попытаюсь проанализировать это, спасибо за помощь. – Mikho

0

Другой подход будет использовать общие IComparer interface и лямбда-выражения.

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);