2010-11-30 13 views
4

Ich habe eine Klasse, die eine List<Action<int>> erstellt und behält sie bis zu einem späteren Zeitpunkt. Diese Klasse kann Delegaten aus dieser Liste hinzufügen und entfernen. Das funktioniert gut, solange die Leute nicht zu extravagant werden. Um die anonyme Funktion zu bekämpfen (die nicht entfernt werden kann), überprüfe ich, ob das Ziel des Delegaten null ist. Wenn es Null ist, werfe ich eine Ausnahme aus. Das Problem tritt auf, wenn ein anonymer Delegat eine Funktion enthält. Dies hat ein Ziel, ist aber genauso nicht zu beseitigen. Der vereinfachte Code unten zeigt meine ProblemeSo identifizieren Sie eine anonyme Funktion

public class MyDelegateContainer 
{ 
    List<Action<int>> m_Container = new List<Action<int>>(); 

    public void Add(Action<int> del) 
    { 
     if (del.Target == null) 
     { 
      throw new Exception("No static handlers"); 
     } 
     m_Container.Add(del); 
    } 

    public bool Remove(Action<int> del) 
    { 
     if (m_Container.Contains(del)) 
     { 
      m_Container.Remove(del); 
      return true; 
     } 

     return false; 
    } 
} 

public class MyFakeActionClass 
{ 
    public void Test(int temp) { } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     bool removed = false; 
     int counter = 0; 
     MyDelegateContainer container = new MyDelegateContainer(); 
     MyFakeActionClass fake = new MyFakeActionClass(); 
     //container.Add(p => { }); //Throws, this is what I want to happen 
     container.Add(fake.Test); //Works, this is the use case 
     removed = container.Remove(fake.Test); //Works, this is the use case 
     Debug.Assert(removed); 
     container.Add(p => { fake.Test(p); counter++; }); //Works but I would like it not to 
     removed = container.Remove(p => { fake.Test(p); counter++; }); //doesn't work 
     Debug.Assert(removed); 
    } 
} 

ich irgendwie müssen identifizieren

p => { fake.Test(p); counter++; } 

ist eine anonyme Funktion, so kann ich aus, wenn jemand es versucht. Danke für jede Hilfe

EDIT: Ich sollte beachten, dass ich eine Action<int> Variable für die anonyme Funktion verwenden könnte und alles würde funktionieren, aber die Hinzufügen und Entfernen sind nie in dem gleichen Umfang in der Praxis.

+0

Wenn es identifiziert wird, ist es nicht anonym. Sind Sie auf der Suche nach einer Schließung, statt? K –

+0

Ich brauche einen Weg, um zu erkennen, dass es eine anonyme Funktion ist – Steve

+0

@Steve - 28 Fragen ohne Antwort aus 83 ?! O_O Und ich sage nur, dass du einen guten Score hast =) – BeemerGuy

Antwort

3

Es gibt keine Möglichkeit, zuverlässig festzustellen, ob eine Funktion "anonym" ist, weil alle Funktionen Namen für die CLR haben. Es ist nur anonym in der Sprache, die es generiert, und das ist compilerabhängig. Sie können möglicherweise den Algorithmus bestimmen, der von dem aktuellen C# -Compiler von Microsoft verwendet wird, nur damit es nicht mehr mit C# 5 oder Mono arbeitet.

Da Sie verhindern möchten, dass Benutzer Ihres Typs Code schreiben, der sie falsch verwendet, müssen Sie nur eine Ausnahme unter Punkt Punkt auslösen, die ihr Programm zum Absturz bringen wird. Was ich tun würde, ist die Ausnahme in der Remove Funktion zu werfen, wenn der Zieldelegat nicht gefunden wird. An diesem Punkt werden Ihre Benutzer immer noch einen Absturz und die einzige Möglichkeit, es zu beheben, besteht darin, den Delegaten in einer Weise zu schreiben, dass es entfernbar ist.

Als zusätzlichen Bonus fangen Sie Fehler, bei denen jemand versucht, Delegierte zweimal zu entfernen oder die nie an erster Stelle hinzugefügt wurden.Der Code würde wie folgt aussehen:

public bool Remove(Action<int> del) 
{ 
    if (m_Container.Contains(del)) 
    { 
     m_Container.Remove(del); 
     return true; 
    } 

    throw new ArgumentException("Attempt to remove nonexistent delegate"); 
} 
+0

bis jetzt scheint dies der beste Weg zu sein gehen. – Steve

+0

+1 für Compiler-abhängige Erklärung. –

1

Ich würde Introspektion verwenden, um die Namen der Methoden zu überprüfen.

Anonyme Methoden haben normalerweise sehr vorhersagbare Namen. (Ich erinnere mich nicht an das genaue Format, aber einige Tests laufen, und es sollte offensichtlich sein).

Der Nachteil wäre, dass, wenn jemand eine nicht-anonyme Methode erstellt, aber beschlossen, es anonMethod123 (oder was auch immer das Format ist ...) Es würde fälschlicherweise zurückgewiesen werden.

+4

Das funktioniert gut , bis der Compiler sich ändert. – Gabe

0

Natürlich können Sie eine anonyme Methode entfernen, Sie müssen nur einen Verweis auf die gleiche anonyme Methode haben.

var myAnonymousMethod = p => { fake.Test(p); counter++; }; 
container.Add(myAnonymousMethod); 
removed = container.Remove(myAnonymousMethod); 
+0

Entschuldigung, mein Edit erklärte, dass diese Dinge nicht im gleichen Umfang in der Regel passieren werden, und ich möchte nur verhindern, dass es vollständig – Steve

+0

@Steve, sollten Sie wirklich starke Gründe, die Verwendung von Teilen der Sprache zu beschränken. Schauen Sie sich zum Beispiel Ereignisse an, Sie können eine anonyme Methode abonnieren, wenn Sie nicht später abmelden wollen, das liegt beim Benutzer. Versuchen Sie, einen anderen Weg zu finden, z. B. eine Methode zum Suchen nach vorhandenen Delegaten anzugeben, die Sie entfernen möchten. –

4

In Ihrem Beispiel ist der Aufrufer verantwortlich für das Entfernen des Handlers. Wenn also der Aufrufer den Handler nicht entfernen möchte, wird er nicht entfernt, unabhängig davon, ob der Handler ein anonymer Delegat/Lambda ist oder nicht.

Mein Vorschlag ist, die Delegaten Behälter etwas zu ändern:

public class MyDelegateContainer 
{ 
    List<Action<int>> m_Container = new List<Action<int>>(); 

    public Action Add(Action<int> del) 
    { 
     m_Container.Add(del); 

     return new Action(() => 
     { 
      m_Container.Remove(del); 
     }); 
    } 
} 

Der Anrufer nach wie vor für das Entfernen des Handler verantwortlich ist, sondern stattdessen die Handler wieder vorbei an den Behälter, erhält er einen „Token "dass es später gespeichert und verwendet werden kann, um den Handler zu entfernen.

+1

Dies ist wahrscheinlich die Art, wie ich gehen würde, wenn ich mit diesem Problem konfrontiert werde. – jonnii

+0

Leider ist dies eine sehr langlebige Klasse und ich bin OK mit Leuten, die sie nicht entfernen. Ich muss nur in der Lage sein, Handler von Zeit zu Zeit dynamisch zu entfernen. – Steve

+2

Das scheint ein bisschen sinnlos. Warum sollten Sie das Token speichern, wenn Sie den ursprünglichen Delegaten nur speichern könnten? – Gabe

0

Wie jonnii in einem Kommentar vorgeschlagen, eine andere Art und Weise Sie es mit einem Wörterbuch implementieren könnte:

public class MyDelegateContainer 
{ 
    Dictionary<string, Action<int>> m_Container = 
     new Dictionary<string, Action<int>>(); 

    public void Add(string key, Action<int> del) 
    { 
     m_Container.Add(key, del); 
    } 

    public bool Remove(string key) 
    { 
     return m_Container.Remove(key); 
    } 
} 

Dann könnte man leicht einen bekannten Delegierten bei einer beliebigen entfernen Punkt in Ihrem Code nur durch das wissen, welchen Namen es hinzuzufügen wurde verwendet:

container.Add("fake.Test", fake.Test); 
    removed = container.Remove("fake.Test"); 
    Debug.Assert(removed); 
    container.Add("anon", p => { fake.Test(p); counter++; }); 
    removed = container.Remove("anon"); // works! 
    Debug.Assert(removed); 
0

alte Frage, die ich weiß, aber ich würde denken, dass dies ein Strom sein würde (und Zukunft) geprüft Art und Weise der Überprüfung, ob ein Verfahren ist, anonym:

bool isAnonymous = !System.CodeDom.Compiler.CodeGenerator.IsValidLanguageIndependentIdentifier(del.Method.Name); 

Der Laufzeitname der anonymen Methode wäre ungültig sein, wenn bei der Kompilierung verwendet, um sicherzustellen, dass sie nicht kollidieren.

Verwandte Themen