2009-03-17 9 views
7

Ich habe versucht, eine Erweiterungsmethode zu schreiben, um List.RemoveAll (Prädikat) nachzuahmen.Erweiterungsmethoden Wörterbuch <TKey,TValue> .RemoveAll? Ist es möglich?

Bisher habe ich diese bekam:

public static void RemoveAll<TKey,TValue>(this Dictionary<TKey,TValue> dict, 
            Predicate<KeyValuePair<TKey,TValue>> condition) 
{ 
    Dictionary<TKey,TValue> temp = new Dictionary<TKey,TValue>(); 

    foreach (var item in dict) 
    { 
     if (!condition.Invoke(item)) 
      temp.Add(item.Key, item.Value); 
    } 

    dict = temp; 
} 

Alle Zeiger? Ist das eine völlig naive Umsetzung?

+0

Möchten Sie nicht Paare aus dem Wörterbuch löschen, indem Sie Ihr Prädikat nur nach Schlüssel und nicht nach KeyValuePair anpassen? – base2

Antwort

16

Ihr Code wird nicht funktionieren, da Sie die Dictionary-Klasse nach Wert übergeben. Dies bedeutet, dass die endgültige Zuweisung (dict = temp) für eine aufrufende Funktion nicht sichtbar ist. In C# ist es nicht erlaubt, die Ziele der Erweiterungsmethode durch ref oder out zu übergeben (in VB ist ByRef erlaubt).

Stattdessen müssen Sie das Wörterbuch inline ändern. Versuchen Sie, die folgenden

public static void RemoveAll<TKey,TValue>(this Dictionary<TKey,TValue> dict, 
            Func<KeyValuePair<TKey,TValue>,bool> condition) 
{ 
    foreach (var cur in dict.Where(condition).ToList()) { 
     dict.Remove(cur.Key); 
    } 
} 

EDIT

Vertauschte die Reihenfolge Wo und ToList die Größe des zugewiesenen Speicher der Liste zu reduzieren. Es wird jetzt nur eine Liste für die Elemente zugeordnet, die entfernt werden sollen.

+0

Hat den Nachteil, dass immer genügend Speicher für die Schlüsselliste zugewiesen wird. aber sicherlich einfach – ShuggyCoUk

+0

Funktioniert eigentlich nicht obwohl ... –

+0

@Rob wie so? Funktioniert gut für Beispieldaten, die ich verwendet habe – JaredPar

4
public static void RemoveAll<TKey,TValue>(
    this Dictionary<TKey,TValue> dict, 
    Predicate<KeyValuePair<TKey,TValue>> condition) 
{ 
    var toRemove = new List<TKey>(); 

    foreach (var item in dict) 
    { 
     if (!condition(item)) 
      toRemove.Add(item); 
    } 
    foreach (var key in toRemove) 
    { 
     dict.Remove(key); 
    } 
} 

Wenn die Anzahl der Tasten ist klein im Verhältnis zu der Größe des Wörterbuchs zu entfernen, dies schneller sein (wenn die entfernte Zahl wahrscheinlich null sein können Sie diese schneller machen, auch durch träge als auch die toremove Liste erstellen.

Dies läuft auf dasselbe wie Jareds aktualisierte Antwort hinaus, erlaubt es Ihnen jedoch, die Erstellung der Entfernungsliste zu verschieben, wenn Sie dies wünschen.Wenn dies kein Problem ist (und Sie keinen Grund haben, den Punkt durch den Prozess zu brechen) dann ist Jareds sauberer und einfacher

+0

Kein Aufruf der condition.Invoke (...) Methode für die Bedingung, weil es bereits eine ist delegieren. Sie können die Bedingung direkt aufrufen, z. Zustand (Gegenstand). – base2

+0

@ base2 Ich habe nur den ursprünglichen Benutzer Stil repliziert. Ich stimme zu, es ist besser ohne die Invoke, aber ich werde es ändern – ShuggyCoUk

1

Diese Methode wird nicht funktionieren, weil der Parameter "dict" nicht von refere übergeben wird nce, und tatsächlich kann nicht sein, weil ref nicht als erster Parameter einer Erweiterungsmethode unterstützt wird.

public static void RemoveAll<TKey,TValue>(this Dictionary<TKey,TValue> dict, 
           Predicate<KeyValuePair<TKey,TValue>> condition) 
{ 
    var temp = new List<TKey>(); 

    foreach (var item in dict) 
    { 
     if (!condition(item)) 
      temp.Add(item.Key); 
    } 

    foreach (var itemKey in temp) 
     dict.Remove(itemKey) 
} 

Ich möchte auch RemoveAllByKey und RemoveAllByValue Implementierungen sehen.

0

Aber wenn Sie wollten, könnten Sie ein neues und anderes Wörterbuch zurückgeben. Ihre Unterschrift würde dies ändern:

public static Dictionary<TKey, TValue> RemoveAll<TKey,TValue>(this Dictionary<TKey,TValue> dict, 
           Predicate<KeyValuePair<TKey,TValue>> condition) 

Und der Angerufene Code würde sagen:

var newDict = oldDict.RemoveAll(kvp=> kvp.Name.StartsWith("something")); 

Und wenn Sie ändern oldDict wollten, würden Sie es so nennen:

oldDict = oldDict.RemoveAll(kvp=> kvp.Name.StartsWith("something")); 
Verwandte Themen