Espressioni C # lambda e IComparer

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

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 l’ordinamento funziona correttamente, la ricerca binaria fornisce un errore di compilazione Imansible convertire l’espressione lambda in modo che digiti “System.Collections.IComparer ” 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 clunky ComparisonComparer per convertire il Confronto in un IComparer:

 public class ComparisonComparer : IComparer { private readonly Comparison comparison; public ComparisonComparer(Comparison comparison) { this.comparison = comparison; } int IComparer.Compare(T x, T y) { return comparison(x, y); } } 

Ciò consente alla ricerca binaria di funzionare come segue:

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

Che schifo. C’è un modo più pulito?

Bene, un’opzione è quella di creare qualcosa come ProjectionComparer . Ne ho una versione in MiscUtil – fondamentalmente crea un IComparer da una proiezione.

Quindi il tuo esempio sarebbe:

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

Oppure potresti implementare i tuoi metodi di estensione su T[] per fare lo stesso genere di cose:

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

Puoi usare la mia ValueComparer :

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

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

Prova questo:

 public static class ComparisonEx { public static IComparer AsComparer(this Comparison @this) { if (@this == null) throw new System.ArgumentNullException("Comparison @this"); return new ComparisonComparer(@this); } public static IComparer AsComparer(this Func @this) { if (@this == null) throw new System.ArgumentNullException("Func @this"); return new ComparisonComparer((x, y) => @this(x, y)); } private class ComparisonComparer : IComparer { public ComparisonComparer(Comparison 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 Comparison { get; private set; } } } 

Ti consente di utilizzare questo codice:

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