So fangen Sie alle Varianten einer generischen Ausnahme in C # ab

22

Ich möchte alle Varianten einer generischen Ausnahmeklasse abfangen und habe mich gefragt, ob es eine Möglichkeit gibt, dies ohne mehrere catch-Blöcke zu tun. Angenommen, ich habe eine Ausnahmeklasse:

public class MyException<T> : Exception
{
    public string MyProperty { get; }

    public MyException(T prop) : base(prop.ToString())
    {
        MyProperty = prop?.ToString();
    }
}

und zwei abgeleitete Klassen:

public class MyDerivedStringException : MyException<string>
{
    public MyDerivedStringException(string prop) : base(prop)
    {

    }
}

public class MyDerivedIntException : MyException<int>
{
    public MyDerivedIntException(int prop) : base(prop)
    {

    }
}

Gibt es eine Möglichkeit, beide MyDerivedStringExceptionund MyDerivedIntExceptionin einem Fangblock zu fangen?

Ich habe das versucht:

try
{
   ...
}

catch(Exception e) when (e is MyDerivedStringException || e is MyDerivedIntException)
{
}

aber es ist nicht sehr sauber und bedeutet, dass ich keinen Zugang zu habe MyProperty.

Ich bin an einer allgemeinen Lösung des Problems interessiert, aber in meinem Fall wird die generische Ausnahme in einer Bibliothek eines Drittanbieters definiert, die, wie unten ausgeführt, dem Problem einige zusätzliche Einschränkungen hinzufügt.

SBFrancies
quelle

Antworten:

15

Machen MyException<T> Sie eine Schnittstelle implementieren und prüfen , ob eine Ausnahme von dem Schnittstellentyp.

Schnittstelle:

public interface IMyException
{
    string MyProperty { get; }
}

Generische Klasse, die die Schnittstelle implementiert:

public class MyException<T> : Exception, IMyException
{
    public string MyProperty { get; }

    public MyException(T prop)
    {
        MyProperty = prop?.ToString();
    }
}

Abgeleitete Klassen:

public class MyDerivedStringException : MyException<string>
{
    public MyDerivedStringException(string prop) : base(prop)
    {

    }
}

public class MyDerivedIntException : MyException<int>
{
    public MyDerivedIntException(int prop) : base(prop)
    {

    }
}

Verwendungszweck:

try
{
    // ...
}
catch (Exception e) when (e is IMyException)
{
    // ...
}

Das Gleiche kann erreicht werden, indem eine Basisklasse erstellt wird Exception, MyException<T>die von dieser Basisklasse erbt und diese dann ableitet .

Eliahu Aaron
quelle
3
Ich bin mir nicht sicher, ob es hier eine Schnittstelle wert ist - nur eine nicht generische Basisklasse zwischen Exceptionund MyException<T>wäre in Ordnung.
Jon Skeet
Außerdem hat OP immer noch keinen Zugriff auf die generische Eigenschaft (ohne Reflektion), was meiner Meinung nach das eigentliche Problem ist.
DavidG
10

Der Test, wenn Ihr Typevon einem Generikum abgeleitet ist, ist:

Type = typeof(something);
t.GetGenericTypeDefinition()==typeof(MyException<>);

Dies gilt jedoch nur für abgeleitete Typen wie MyException<int>oder MyException<string>.

Wenn Sie weitere Derivate haben, an denen MyDerivedStringExceptionSie testen mussten:

ex.GetType.BaseType.GetGenericTypeDefinition()==typeof(MyException<>);

Dies funktioniert also für jedes vorhandene Generikum, aber Sie müssen die Vererbungsstufe für diesen Test kennen oder alle Basistypen durchlaufen.

Sie könnten also Folgendes tun:

catch(Exception ex) when (ex.GetType.BaseType.GetGenericTypeDefinition()==typeof(MyException<>))
Holger
quelle
3

Diese Implementierung wird aktiviert und deaktiviert, wenn T: Value der Wertetyp ist. In Bezug auf die Auslastung können Sie jedoch die Auswirkungen auf die Leistung anhand der Häufigkeit steuern, mit der Sie versuchen, die Box zu öffnen oder zu entfernen.

public class MyException<T> : MyException
{
    public T Prop => (T)base.Prop;

    public MyException(T prop) : base(prop)
    {

    }
}

public class MyException : Exception
{
    protected object Prop { get; }

    public MyException(object prop)
    {
         Prop = prop;
    }
}

Logik

try {
     ...
} catch(MyException e) {
    ...
}
Trae Moore
quelle
Dies ist eine gute Antwort, die ich positiv bewertet habe. Leider löst sie mein spezifisches Problem nicht, da sich die generische Ausnahme in einer Bibliothek eines Drittanbieters befindet und ich sie daher nicht bearbeiten kann.
SBFrancies
2

Leider kann man mit dem nicht fangen

catch(MyException ex) Klasse, wie es einen Typ erwartet.

Am besten erstellen Sie eine Basisklasse oder Schnittstelle, fangen sie ab und holen den Typ von dort ab.

Es gibt bereits eine Antwort darauf , wie Sie den Typ erhalten und dies tun können.

Athanasios Kataras
quelle
1

Diese Implementierung abstrahiert den Handhabungsdelegierten für den Ausnahmetyp vom Kontext der AGB ... Dies mag etwas zu abenteuerlich sein, aber das Coole daran ist, dass Sie je nach Umfang der Wiederverwendung und Kontext von verschiedene Handler einfügen können Verwertung.

public interface IExceptionHandler
{
    object Handle(Exception ex);

    void RegisterExceptionTypeHandler<T>(Func<T,object> handlerDelegate) where T : Exception;
}

Registrierungslogik

handler.RegisterExceptionTypeHandler<MyException<int>>(ex => ...);
handler.RegisterExceptionTypeHandler<MyException<string>>(ex => ...);

try {
    ...
}catch(Exception ex)
{
    ...any validation
    return exceptionHandler.Handle(ex)
}
Trae Moore
quelle