47

Ich habe zufällig Code gesehen, wo dieser Typ einen Lambda-Ausdruck an eine ArrayList.Sort (IComparer hier) oder eine IEnumerable.SequenceEqual (IEnumerable Liste, IEqualityComparer hier) übergeben hat, wo ein IComparer oder IEqualityComparer erwartet wurde.Übergeben Sie einen Lambda-Ausdruck anstelle von IComparer oder IEqualityComparer oder einer anderen Methode-Schnittstelle?

Ich kann nicht sicher sein, ob ich es sah, oder ich träume nur. Und ich kann anscheinend keine Erweiterung für eine dieser Sammlungen finden, die eine Func <> oder einen Delegaten in ihren Methodensignaturen akzeptiert.

Gibt es eine solche Überladungs-/Erweiterungsmethode? Oder, wenn nicht, ist es möglich, so herumzureißen und einen Algorithmus (Lese-Delegat) zu übergeben, wo eine Schnittstelle mit einer einzigen Methode erwartet wird?

Aktualisieren Danke, jeder. Das ist was ich dachte. Ich muss geträumt haben. Ich weiß, wie man eine Konvertierung schreibt. Ich war mir nur nicht sicher, ob ich so etwas gesehen oder gedacht hatte, ich hätte es gesehen.

Noch ein Update Schauen Sie, hier habe ich eine solche Instanz gefunden. Ich habe nicht geträumt. Schauen Sie sich what this guy is doing here an. Was gibt?

Und hier ist ein weiteres Update: Ok, ich verstehe. Der Typ benutzt die Comparison<T> Überladung. Nett. Nett, aber total anfällig, um dich in die Irre zu führen. Guter Gedanke. Vielen Dank.

+1

mögliches Duplikat von [Wrap einen Delegaten in einem IEqualityComparer] (http://stackoverflow.com/questions/98033/wrap-a-delegate-in-an-iequalitycomparer) – nawfal

+0

@ Nawfal: Das ist eine andere Frage. Sie sind etwas verwandt, aber immer noch anders. Das ist eine sehr schöne Frage. Danke für das Teilen. Ich fand es sehr interessant. :-) –

+0

Ach ja, ich sehe jetzt, aber sehr nahe: P Ich habe die enge Abstimmung zurückgezogen, aber ich werde den Kommentar behalten, damit andere Besucher bemerken. Eine Sache, bitte akzeptiere Antworten. Ich denke, dass die am besten gewählte Antwort Ihre Frage beantwortet. – nawfal

Antwort

3

Ich stimme für die Traumtheorie.

Sie können eine Funktion nicht übergeben, wenn ein Objekt erwartet wird: Derivate von System.Delegate (was Lambda ist) implementieren diese Schnittstellen nicht.

Was Sie wahrscheinlich gesehen haben, ist eine Verwendung der Converter<TInput, TOutput> Delegate, die von einem Lambda modelliert werden kann. Array.ConvertAll verwendet eine Instanz dieses Delegaten.

10

Sie können ein Lambda für eine Array.Sort-Methode angeben, da hierfür eine Methode erforderlich ist, die zwei Objekte vom Typ T akzeptiert und eine ganze Zahl zurückgibt. Als solches könnten Sie ein Lambda der folgenden Definition angeben: (a, b) => a.CompareTo(b). Ein Beispiel einer absteigenden Art eines Integer-Array zu tun:

int[] array = { 1, 8, 19, 4 }; 

// descending sort 
Array.Sort(array, (a, b) => -1 * a.CompareTo(b)); 
+0

Können Sie das weiter erklären? Ist dies ein Verhalten, das nur für die IComparer-Schnittstelle gilt, oder funktioniert es an einer Schnittstelle, die nur eine einzige Methode enthält? – StriplingWarrior

+6

@Stripling, glaube ich, dass dies tatsächlich die Überladung verwendet, die ein 'Comparison ' akzeptiert, da 'Comparison' ein Delegat ist, das die zwei Parameter akzeptiert und die Ganzzahl zurückgibt. Daher eignet sich die Bereitstellung eines gültigen Lambda für diese Überladung. –

2

Diese Methoden haben keine Überlastungen, die einen Delegierten statt einer Schnittstelle akzeptieren, aber:

  • Sie können in der Regel eine einfachere Art zurückkehren Schlüssel durch die Delegierten übergeben Sie Enumerable.OrderBy
  • Ebenso Sie Enumerable.Select vor dem Aufruf Enumerable.SequenceEqual
  • Es sollte einen Wrapper zu schreiben, die implementierteinfach sein nennen könntenin Bezug auf Func<T, T, bool>
  • F # Sie diese Art von Schnittstelle in Form einer Lambda :)
  • es
3

Sie können nicht passieren direkt umsetzen können jedoch Sie eine LambdaComparer Klasse definieren, die eine Func<T,T,int> excepts so tun konnte, und benutzt dann das in ihm CompareTo.

Es ist nicht ganz so knapp, aber Sie könnten es durch einige kreative Erweiterungsmethoden auf Func kürzer machen.

5
public class Comparer2<T, TKey> : IComparer<T>, IEqualityComparer<T> 
{ 
    private readonly Expression<Func<T, TKey>> _KeyExpr; 
    private readonly Func<T, TKey> _CompiledFunc 
    // Constructor 
    public Comparer2(Expression<Func<T, TKey>> getKey) 
    { 
     _KeyExpr = getKey; 
     _CompiledFunc = _KeyExpr.Compile(); 
    } 

    public int Compare(T obj1, T obj2) 
    { 
     return Comparer<TKey>.Default.Compare(_CompiledFunc(obj1), _CompiledFunc(obj2)); 
    } 

    public bool Equals(T obj1, T obj2) 
    { 
     return EqualityComparer<TKey>.Default.Equals(_CompiledFunc(obj1), _CompiledFunc(obj2)); 
    } 

    public int GetHashCode(T obj) 
    { 
     return EqualityComparer<TKey>.Default.GetHashCode(_CompiledFunc(obj)); 
    } 
} 

verwenden Sie es wie dieser

ArrayList.Sort(new Comparer2<Product, string>(p => p.Name)); 
+2

Warum akzeptieren Sie nicht 'Func ' '? – AlexFoxGill

+0

[Einfach durch 'Func '] (http://stackoverflow.com/a/1239337/2122718), aber danke für die Verwendung von Exprestion – marbel82

18

Ich bin nicht viel sicher, was nützlich es wirklich ist, wie ich es für die meisten Fälle in der Basisbibliothek denken eine IComparer erwartet eine Überlastung gibt es, dass ein Vergleich erwartet ... aber nur für das Protokoll:

in .Net 4.5 sie eine Methode hinzugefügt hat eine IComparer aus einem Vergleich zu erhalten: Comparer.Create

so können Sie Ihr Lambda an es übergeben und einen IComparer erhalten.

+0

Es wäre relativ einfach, eine Extension-Methode zu .NET 4 hinzuzufügen, die vollbringt dasselbe. – jessehouwing

+4

Gibt es etwas Ähnliches für EqualityComparer? Es hat keine Create-Methode, aber es scheint sehr seltsam, diese nützliche Methode für Comparer hinzuzufügen und nicht EqualityComparer – rdans

+2

@rdans 'EqualityComparer' verwendet andere Signatur und verwendet auch' GetHashCode'-Methode. Sie können also nicht einfach einen Vergleich dafür erstellen. – VMAtm

16

Ich googelte auch das Internet nach einer Lösung, aber ich fand keine befriedigende. Also habe ich eine generische EqualityComparerFactory erstellt:

public static class EqualityComparerFactory<T> 
{ 
    private class MyComparer : IEqualityComparer<T> 
    { 
     private readonly Func<T, int> _getHashCodeFunc; 
     private readonly Func<T, T, bool> _equalsFunc; 

     public MyComparer(Func<T, int> getHashCodeFunc, Func<T, T, bool> equalsFunc) 
     { 
      _getHashCodeFunc = getHashCodeFunc; 
      _equalsFunc = equalsFunc; 
     } 

     public bool Equals(T x, T y) 
     { 
      return _equalsFunc(x, y); 
     } 

     public int GetHashCode(T obj) 
     { 
      return _getHashCodeFunc(obj); 
     } 
    } 

    public static IEqualityComparer<T> CreateComparer(Func<T, int> getHashCodeFunc, Func<T, T, bool> equalsFunc) 
    { 
     if (getHashCodeFunc == null) 
      throw new ArgumentNullException("getHashCodeFunc"); 
     if (equalsFunc == null) 
      throw new ArgumentNullException("equalsFunc"); 

     return new MyComparer(getHashCodeFunc, equalsFunc); 
    } 
} 

Die Idee ist, dass die CreateComparer Methode zwei Argumente nimmt: einen Delegaten GetHashCode (T) und einem Vertreter der Equals (T, T)

Beispiel :

class Person 
{ 
    public int Id { get; set; } 
    public string LastName { get; set; } 
    public string FirstName { get; set; } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var list1 = new List<Person>(new[]{ 
      new Person { Id = 1, FirstName = "Walter", LastName = "White" }, 
      new Person { Id = 2, FirstName = "Jesse", LastName = "Pinkman" }, 
      new Person { Id = 3, FirstName = "Skyler", LastName = "White" }, 
      new Person { Id = 4, FirstName = "Hank", LastName = "Schrader" }, 
     }); 

     var list2 = new List<Person>(new[]{ 
      new Person { Id = 1, FirstName = "Walter", LastName = "White" }, 
      new Person { Id = 4, FirstName = "Hank", LastName = "Schrader" }, 
     }); 


     // We're comparing based on the Id property 
     var comparer = EqualityComparerFactory<Person>.CreateComparer(a => a.Id.GetHashCode(), (a, b) => a.Id==b.Id); 
     var intersection = list1.Intersect(list2, comparer).ToList(); 
    } 
} 
1

Im Fall, wenn Sie diese Funktion für die Verwendung mit Lambda und möglicherweise zwei verschiedene Elementtypen benötigen:

static class IEnumerableExtensions 
{ 
    public static bool SequenceEqual<T1, T2>(this IEnumerable<T1> first, IEnumerable<T2> second, Func<T1, T2, bool> comparer) 
    { 
     if (first == null) 
      throw new NullReferenceException("first"); 

     if (second == null) 
      throw new NullReferenceException("second"); 

     using (IEnumerator<T1> e1 = first.GetEnumerator()) 
     using (IEnumerator<T2> e2 = second.GetEnumerator()) 
     { 
      while (e1.MoveNext()) 
      { 
       if (!(e2.MoveNext() && comparer(e1.Current, e2.Current))) 
        return false; 
      } 

      if (e2.MoveNext()) 
       return false; 
     } 

     return true; 
    } 
} 
Verwandte Themen