2011-02-02 14 views
17

Sto usando espressioni lambda per ordinare e cercare una matrice in C#. Non voglio implementare l'interfaccia IComparer nella mia classe, perché ho bisogno di ordinare e cercare su più campi membro.Espressioni lambda C# e IComparer

class Widget 
{ 
    public int foo; 

    public void Bar() 
    { 
     Widget[] widgets; 

     Array.Sort(widgets, (a, b) => a.foo.CompareTo(b.foo)); 

     Widget x = new Widget(); 
     x.foo = 5; 
     int index = Array.BinarySearch(widgets, x, 
             (a, b) => a.foo.CompareTo(b.foo)); 
    } 
} 

Mentre il genere funziona bene, la ricerca binaria dà un errore di compilazione Impossibile convertire espressione lambda al tipo 'System.Collections.IComparer <Widget>' perché non è un tipo delegato. Per qualche ragione, Sort ha overload per IComparer e Comparison, ma BinarySearch supporta solo IComparer. Dopo alcune ricerche, ho scoperto il goffo ComparisonComparer<T> per convertire il confronto a un IComparer:

public class ComparisonComparer<T> : IComparer<T> 
{ 
    private readonly Comparison<T> comparison; 

    public ComparisonComparer(Comparison<T> comparison) 
    { 
     this.comparison = comparison; 
    } 

    int IComparer<T>.Compare(T x, T y) 
    { 
     return comparison(x, y); 
    } 
} 

Questo permette la ricerca binaria per lavorare come segue:

int index = Array.BinarySearch(
    widgets, 
    x, 
    new ComparisonComparer<Widget>((a, b) => a.foo.CompareTo(b.foo))); 

Yuck. C'è un modo più pulito?

+4

Il .NET4.5 prossimo ha un metodo 'Comparer <> create' per la costruzione di un' IComparer <>. 'istanza da un delegato' IComparison <> '. –

risposta

9

Bene, un'opzione è creare qualcosa come ProjectionComparer invece. Ne ho una versione in MiscUtil - in pratica crea uno IComparer<T> da una proiezione.

Così il vostro esempio può essere:

int index = Array.BinarySearch(widgets, x, 
           ProjectionComparer<Widget>.Create(x => x.foo)); 

Oppure si potrebbe implementare i propri metodi di estensione su T[] di fare la stessa cosa:

public static int BinarySearchBy<TSource, TKey>(
    this TSource[] array, 
    TSource value, 
    Func<TSource, TKey> keySelector) 
{ 
    return Array.BinarySearch(array, value, 
           ProjectionComparer.Create(array, keySelector)); 
} 
+5

Per la cronaca, ProjectionComparer , ValueComparer e ComparisonEx hanno un bell'aspetto. Alla fine della giornata, però, mi chiedo perché alcuni metodi incorporati accettano Comparison e IComparer, e altri solo IComparer ... –

8

Puoi usare il mio ValueComparer<T> class:

int index = Array.BinarySearch(
    widgets, x, 
    new ValueComparer<Widget>(x => x.Foo) 
); 

È possibile confrontare per più proprietà passando più espressioni lambda.

3

Prova questo:

public static class ComparisonEx 
{ 
    public static IComparer<T> AsComparer<T>(this Comparison<T> @this) 
    { 
     if (@this == null) 
      throw new System.ArgumentNullException("Comparison<T> @this"); 
     return new ComparisonComparer<T>(@this); 
    } 

    public static IComparer<T> AsComparer<T>(this Func<T, T, int> @this) 
    { 
     if (@this == null) 
      throw new System.ArgumentNullException("Func<T, T, int> @this"); 
     return new ComparisonComparer<T>((x, y) => @this(x, y)); 
    } 

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

     public int Compare(T x, T y) 
     { 
      return this.Comparison(x, y); 
     } 

     public Comparison<T> Comparison { get; private set; } 
    } 
} 

Esso consente di utilizzare questo codice:

Comparison<int> c = (x, y) => x == y ? 0 : (x <= y ? -1 : 1); 
IComparer<int> icc = c.AsComparer(); 

Func<int, int, int> f = (x, y) => x == y ? 0 : (x <= y ? -1 : 1); 
IComparer<int> icf = f.AsComparer(); 
+1

ArgumentNullException accetta un nome parametro, senza un tipo. – SLaks

+0

Questo è ciò di cui avevo bisogno. thanx – nima

Problemi correlati