2011-02-02 25 views
17

Używam wyrażeń lambda do sortowania i wyszukiwania tablicy w języku C#. Nie chcę implementować interfejsu IComparer w mojej klasie, ponieważ muszę sortować i przeszukiwać wiele pól elementów.C# wyrażeń lambda i 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)); 
    } 
} 

Podczas sortowania działa prawidłowo, wyszukiwanie binarne daje błąd kompilacji nie można przekonwertować wyrażenia lambda wpisać „System.Collections.IComparer <widget>”, ponieważ nie jest to typ delegata. Z jakiegoś powodu Sort ma przeciążenia zarówno dla programu IComparer, jak i dla porównania, ale BinarySearch obsługuje tylko program IComparer. Po kilku badań odkryłem przylegający ComparisonComparer<T> przekonwertować porównanie do 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); 
    } 
} 

Umożliwia wyszukiwanie binarne do pracy w następujący sposób:

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

fuj. Czy istnieje czystszy sposób?

+4

Nadchodzący .NET4.5 ma metodę 'porównywarka <> Create' do konstruowania' IComparer <>. 'wystąpienie z delegata' IComparison <>. –

Odpowiedz

9

Jedną z opcji jest utworzenie czegoś takiego jak ProjectionComparer. Mam wersję tego w MiscUtil - w zasadzie tworzy ona IComparer<T> z projekcji.

Więc Przykładem może być:

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

Albo można zaimplementować własne metody rozszerzenie na T[] do tego samego rodzaju rzeczy:

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

Dla rekordu, ProjectionComparer , ValueComparer i ComparisonEx wszystkie wyglądają dobrze. Jednak pod koniec dnia zastanawiam się, dlaczego niektóre wbudowane metody akceptują Porównanie i IComparer, a inne tylko IComparer ... –

8

Można używać mojego ValueComparer<T> class:

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

Możesz porównać wiele właściwości, przekazując wiele wyrażeń lambda.

3

Spróbuj tego:

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

To pozwala użyć tego kodu:

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 przyjmuje nazwę parametru, bez typu. – SLaks

+0

To jest to, czego potrzebowałem. thanx – nima

Powiązane problemy