2013-04-02 4 views
7

Ich habe viele Methoden, die einige Protokollierung mit dem gleichen Muster erfordern. Einige Methoden müssen einen Wert zurückgeben, andere nicht. Ich habe eine Methode mit Aktionsparameter erstellt, um zu vermeiden, dass die gesamte Logik kopiert wird. Es sieht wie folgt aus:Kombinieren von Aktion und Func in einem Parameter

private void Execute(Action action) 
{ 
    Logger.Start(); 
    try 
    { 
     action(); 
    } 
    catch(Exception exception) 
    { 
     Logger.WriteException(); 
     throw; 
    } 
    finally 
    { 
     Logger.Finish(); 
    } 
} 

Jetzt habe ich einige Anrufe, die wie die

public void DoSomething(string parameter) 
{ 
    Execute(() => GetProvider(parameter).DoSomething()); 
} 

Aber ich eine Funktion benötigen, die Werte zurückgeben. Was sind die besten Möglichkeiten, dies zu tun? Ich habe zwei jetzt gefunden:

1) Erstellen Sie eine Kopie der Execute-Methode mit Func

private T Execute<T>(Func<T> action) 
{ 
    Logger.Start(); 
    try 
    { 
     return action(); 
    } 
    catch(Exception exception) 
    { 
     Logger.WriteException(); 
     throw; 
    } 
    finally 
    { 
     Logger.Finish(); 
    } 
} 

Dies funktioniert Methode hat aber auch einige Kopie einfügen.

2) Trick die Parameter in einer Aktion sein:

public Result DoSomething(string parameter) 
{ 
    Result result = null; 
    Execute(() => result = GetProvider(parameter).DoSomething()); 
    return result; 
} 

Diese nicht kopieren und einfügen erfordert aber sieht nicht so schön.

Gibt es eine Möglichkeit, Action und Func beizutreten, um irgendeine dieser Methoden zu vermeiden oder gibt es eine andere Möglichkeit, dasselbe Ergebnis zu erzielen?

+0

Ich benutze die zweite Ihrer Ansätze. Ich konnte keinen guten Weg finden, es anders zu machen, also würde ich mich freuen, Antworten auf deine Frage zu sehen! –

+0

Es scheint damit verbunden zu sein: http://StackOverflow.com/q/4279210/55209 –

+0

betrachten Aspekte verwenden http://StackOverflow.com/Questions/1416880/aspect-oriented-Programming-in-c-sharp – Lanorkin

Antwort

6

ist eine dritte Option noch Execute überlasten, aber die Action Version Arbeit in Bezug auf die Func Version machen:

private void Execute(Action action) 
{ 
    // We just ignore the return value here 
    Execute(() => { 
     action(); 
     return 0; 
    }); 
} 

Natürlich alles einfacher wäre, wenn void mehr waren wie ein „echter“ Typ (wie Unit in F # et al), an welchem ​​Punkt wir nur Task<T> statt Task und Task<T> auch haben könnte ...

+0

Ich dachte auch an diese Methode, habe sie aber nicht einmal hinzugefügt, da sie mir ziemlich dreckig vorkommt, weil Sie vorgeben, ein Func zu sein, indem Sie int für einen generischen Typ verwenden und den Standardwert zurückgeben, der nie benutzt wird. –

+1

@IlyaChernomordik: Ich denke, das ist sauberer als die Zuweisung zu einer lokalen Variablen innerhalb einer 'Aktion', um es wie ein' Func ' zu machen, persönlich. Aber der Punkt ist, dass es innerhalb von 'Execute' liegt, nicht innerhalb von' DoSomething' - Sie brauchen diesen "Dreck" nur an einem einzigen Ort, nicht in mehreren Anrufern. –

+0

@ JonSkeet Vielleicht hast du Recht und es ist weniger schmutzig. Oder kann das gleiche schmutzig aber an einem Ort sein :) –

3

erstellen einer Kopie von Execute, die die Func int konvertiert o ein Action. Sie haben nur einmal den hässlichen Code zu schreiben, und Sie müssen nicht mit einer kompletten zweiten Kopie der Execute Methode am Ende:

private T Execute<T>(Func<T> func) 
{ 
    T result = default(T); 
    this.Execute(() => { result = func(); }); 
    return result; 
} 

... 

public Result DoSomething(string parameter) 
{ 
    return Execute(() => GetProvider(parameter).DoSomething()); 
} 
+0

@ p.s.w.g Das Problem mit diesem Ansatz ist, dass ich beide Werttypen und Klassen als Rückgabe habe. Und Ihr Code wird nicht kompiliert, da die lokale Variable vor der Rückgabe nicht initialisiert wird. –

+0

@IlyaChernomordik Initialisiere 'result' einfach auf' default (T) '. Siehe meine aktualisierte Antwort. –

+0

@ p.s.w.g Richtig, dachte nicht über Standard eins. Eigentlich ist dieses Update ziemlich logisch für meine zweite Methode, weiß nicht, warum ich nicht darüber nachgedacht habe. –

2

Hier ist eine weitere Option. Anstatt das Protokollierungs-Framework Ihren tatsächlichen Code aufrufen zu lassen, rufen Sie den Protokollierungs-Framework auf. So etwas würde den Trick (stark vereinfacht) machen.

public class LoggerScope : IDisposable { 

    private bool disposed; 

    public LoggerScope() { 
     Logger.Start(); 
    } 

    public void Dispose() { 
     if(!disposed) { 
      Logger.Finish(); 
      disposed = true; 
     } 
    } 
} 

wie folgt verwendet:

 using(var scope = new LoggerScope()) { 
      // actual code goes here 
     } 

Griff Ausnahmen separat durch den Fang und sie nur einmal auf der obersten Ebene des Codes anzumelden.

Vorteile:

  • Vermeiden die Notwendigkeit lambdas ganz über den Platz, so dass der Ausnahme-Stack-Trace ist ein bisschen sauberer.
  • Sie können der Klasse beliebige kontextbezogene Daten hinzufügen, z. GUID, Timestamp, logischer Aufgabenbeschreibungstext.
+0

Der Nachteil ist, dass Sie für jeden Spezialfall eine spezielle Klasse erstellen müssen. Obwohl ich diesen Ansatz für die Schreibsperre verwendet habe. –

Verwandte Themen