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.