2 Stimmen

Einheitliche Fehlerbehandlung großer Schnittstellen

Angenommen, ich habe eine .NET-Assembly A, die Reflektion verwendet, um die Assemblys B und C (auch .NET) zu laden. Diese beiden Assemblys implementieren beide eine Reihe großer Schnittstellen (die für beide gleich sind). Wenn eine Methode in A aufgerufen wird, versucht sie, dass B die Arbeit erledigt. B ist jedoch unzuverlässig und könnte Ausnahmen werfen. Jetzt hat A zwei Modi, einen, in dem es die Ausnahmen an den Aufrufer von A weitergibt, und einen, in dem es passende Methoden bei dem stabileren (aber weniger performanten) C aufruft.

Gibt es bessere Möglichkeiten (weniger Code), um dieses Szenario zu implementieren, als alle Methoden, die B öffentlich macht, in einer großen Implementierung aller Schnittstellen von B zu umhüllen, wobei jetzt jeder Aufruf mit dem unten gezeigten Code umgeben ist? Die Assemblys B und C wissen nichts über den gewählten Ansatz zur Fehlerbehandlung, daher können sie die Logik nicht implementieren.

public class BErrorWrapper : I1, I2, I3{
   ...
   public int DoSomeWork(int num){
      if (FailWithExceptions)
      {
         try
         {
            return B.DoSomeWork(num);
         }
         catch(MyLibException e)
         {
            return C.DoSomeWOrk(num);
         }
      }
      else
      {
         return B.DoSomeWork(num);
      }
   }
   ...
}

2voto

1800 INFORMATION Punkte 125009

Sie könnten das tun, es spart etwas Wiederholung:

public class BErrorWrapper : I1, I2, I3{
   ...
   public int DoSomeWork(int num){
      try
      {
         return B.DoSomeWork(num);
      }
      catch(MyLibException e)
      {
         if (FailWithExceptions) throw;
         return C.DoSomeWOrk(num);
      }
   }
   ...
}

1voto

Marc Gravell Punkte 970173

Hmmm... um etwas Code zu sparen, könnten Sie die meisten der gemeinsamen Signaturen über Delegaten umschließen, wie unten (Anmerkung: Ich habe nur die Kernpunkte aufgenommen, um es übersichtlich zu halten - aber Sie können Ihre FailWithExceptions-Sachen trivial hinzufügen):

    static void TryWithFallback(Action primary, Action fallback, T arg)
    {
        try
        {
            primary(arg);
        }
        catch // fügen Sie hier Ihren anderen regulären Code hinzu...
        {
            fallback(arg);
        }
    }

Dann können Sie dies für Implementierungen erneut verwenden über:

    TryWithFallback(b.DoSomeWork, c.DoSomeWork, num);

Selbstverständlich könnten Sie noch einige Signaturen für ähnlichen Code hinzufügen, wie zum Beispiel:

    static TResult TryWithFallback(Func primary, Func fallback, T arg)
    {
        try
        {
            return primary(arg);
        }
        catch
        {
            return fallback(arg);
        }
    }

1voto

user24770 Punkte 49

Ich schlage vor, dass Sie die Verwendung von Reflexion und System.CodeDom in Betracht ziehen, um den Code zu generieren. Ich hatte kürzlich ein ähnliches Problem, als ich versuchte, einen Wrapper für eine COM-Interop-Assembly zu schreiben, die eine sehr große Schnittstelle mit vielen Methoden hatte, deren Signaturen Parameter enthielten, die ich auf der Client-Seite nicht verwenden wollte, die ich jedoch auf der Client-Seite leicht in die gewünschten Typen konvertieren konnte. Es handelte sich auch um 'ref'-Parameter, die den Client-Code umfangreicher machen würden. Die Methoden hatten unterschiedliche Anzahlen von Parametern mit aussagekräftigen Namen, die ich auf der Client-Seite freigeben wollte.

Schreiben Sie eine Basisklasse für Ihren Wrapper, die Mitglieder für eine Instanz von A und B hat. Schreiben Sie eine Codegenerierungsklasse, die eine abgeleitete Wrapper-Klasse generiert. Die Codegenerierung sollte dann über die Methoden für jedes Interface iterieren, das der Wrapper implementieren sollte, und eine Methode und Methode mit entsprechendem Code hinzufügen, um A und B innerhalb Ihrer erforderlichen Fehlerbehandlungskonstruktion aufzurufen. Der von Ihnen generierte Code, um A und B aufzurufen, hängt von der Signatur der Methode ab, die Sie aufrufen, aber dies ist leicht durch Iteration über die Parameter der generierten oder aufgerufenen Methode zu erreichen.

Ich könnte meinen Code posten, aber da ich etwas leicht unterschiedliches mache, denke ich, dass Sie besser zu MSDN oder anderen öffentlichen Beispielen zurückgehen sollten. Alles, was ich sagen kann, ist, dass ich das überraschend unkompliziert und robust gefunden habe.

Ich schlage vor, dass Sie Ihren Codegenerierungscode überprüfen, anstatt den generierten Code, und die Codegenerierung als Teil Ihres Builds ausführen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X