2011-02-02 19 views
17

Estoy usando expresiones lambda para ordenar y buscar una matriz en C#. No quiero implementar la interfaz IComparer en mi clase, porque necesito ordenar y buscar en múltiples campos de miembros.C# expresiones lambda y 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)); 
    } 
} 

Mientras que el tipo funciona bien, la búsqueda binaria da un error de compilación No se puede convertir la expresión lambda para escribir 'System.Collections.IComparer <Widget>' porque no es un tipo de delegado. Por alguna razón, Sort tiene sobrecargas tanto para IComparer como para comparación, pero BinarySearch solo es compatible con IComparer. Después de algunas investigaciones, descubrí la torpe ComparisonComparer<T> para convertir la comparación con 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); 
    } 
} 

Esto permite que la búsqueda binaria para trabajar de la siguiente manera:

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

Yuck. ¿Hay una manera más limpia?

+4

La próxima .NET4.5 tiene un método 'Comparer <> create' para la construcción de una' IComparer <>. 'instancia de un delegado' IComparison <> '. –

Respuesta

9

Bueno, una opción es crear algo como ProjectionComparer en su lugar. Tengo una versión de eso en MiscUtil - básicamente crea un IComparer<T> de una proyección.

Así que su ejemplo podría ser:

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

o usted podría aplicar sus propios métodos de extensión en T[] para hacer el mismo tipo de cosas:

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

Para el registro, ProjectionComparer , ValueComparer , y ComparisonEx se ven bien. Sin embargo, al final del día, me pregunto por qué algunos métodos incorporados aceptan Comparison e IComparer, y otros simplemente IComparer ... –

8

Usted puede usar mi ValueComparer<T> class:

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

Puedes pasar por múltiples propiedades pasándola múltiples expresiones lambda.

3

Prueba esto:

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

Le permite utilizar este código:

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 toma el nombre de un parámetro, sin un tipo. – SLaks

+0

Esto es lo que necesitaba. gracias – nima