Come catturare tutte le varianti di un'eccezione generica in C #


22

Vorrei catturare tutte le varianti di una classe di eccezione generica e mi chiedevo se esiste un modo per farlo senza più blocchi di cattura. Ad esempio dire che ho una classe di eccezione:

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

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

e due classi derivate:

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

    }
}

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

    }
}

c'è un modo per catturare entrambi MyDerivedStringExceptione MyDerivedIntExceptionin un blocco di cattura.

Ho provato questo:

try
{
   ...
}

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

ma non è molto pulito e significa che non ho accesso a MyProperty.

Sono interessato a una soluzione generale al problema, ma nel mio caso l'eccezione generica è definita in una libreria di terze parti che, come indicato di seguito, aggiunge alcuni ulteriori vincoli al problema.

Risposte:


15

Rendere MyException<T>implementare un'interfaccia e verificare un'eccezione in base al tipo di interfaccia.

Interfaccia:

public interface IMyException
{
    string MyProperty { get; }
}

Classe generica che implementa l'interfaccia:

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

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

Classi derivate:

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

    }
}

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

    }
}

Uso:

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

Lo stesso si può fare creando una classe base che eredita Exceptione facendo MyException<T>derivare da quella classe base.


3
Non sono sicuro che valga la pena un'interfaccia qui - solo una classe di base non generica tra Exceptione MyException<T>andrebbe bene.
Jon Skeet,

Inoltre, OP non ha ancora accesso alla proprietà generica (senza usare la riflessione) che credo sia il vero problema.
DavidG,

10

Il test se il tuo Typeè derivato da un generico è:

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

Ma questo è vero solo per i tipi derivati ​​stessi, come MyException<int>o MyException<string>.

Se hai altri derivati ​​come quelli su cui MyDerivedStringExceptionhai dovuto testare:

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

Quindi funziona per qualsiasi generico esistente, ma è necessario conoscere il livello di eredità per questo test o scorrere tutti i tipi di base.

Quindi potresti farlo:

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

3

Questa implementazione inscatola e deseleziona se T: Value è Valuetype ... ma per quanto riguarda l'utilizzo è possibile controllare le implicazioni delle prestazioni con il numero di volte in cui si tenta di box / unbox.

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;
    }
}

Logica

try {
     ...
} catch(MyException e) {
    ...
}

Questa è una buona risposta e ho votato a fondo - purtroppo non risolve il mio problema specifico in quanto l'eccezione generica si trova in una libreria di terze parti, quindi non posso modificarla.
SBFrancies,

2

Sfortunatamente, non si può prendere con il

catch(MyException ex) classe, poiché prevede un tipo.

La tua scommessa migliore sarebbe quella di creare una classe base o un'interfaccia, prenderla e ottenere il tipo da lì.

C'è già una risposta, quindi qui su come ottenere il tipo e farlo.


1

questa implementazione estrae il delegato della gestione per il tipo di eccezione lontano dal contesto del T / C ... Questo può essere un po 'troppo avventuroso, ma la parte interessante è che puoi iniettare gestori diversi a seconda dell'ambito del riutilizzo e del contesto di utilizzo.

public interface IExceptionHandler
{
    object Handle(Exception ex);

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

logica di registrazione

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

try {
    ...
}catch(Exception ex)
{
    ...any validation
    return exceptionHandler.Handle(ex)
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.