web-dev-qa-db-fra.com

Comment intercepter toutes les variantes d'une exception générique en C #

Je voudrais attraper toutes les variantes d'une classe d'exception générique et je me demandais s'il y avait un moyen de le faire sans plusieurs blocs catch. Par exemple, disons que j'ai une classe d'exception:

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

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

et deux classes dérivées:

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

    }
}

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

    }
}

existe-t-il un moyen d'attraper à la fois MyDerivedStringException et MyDerivedIntException dans un bloc catch.

J'ai essayé ceci:

try
{
   ...
}

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

mais ce n'est pas très propre et signifie que je n'ai pas accès à MyProperty.

Je suis intéressé par une solution générale au problème, mais dans mon cas, l'exception générique est définie dans une bibliothèque tierce qui, comme indiqué ci-dessous, ajoute des contraintes supplémentaires au problème.

22
SBFrancies

Faire MyException<T> implémente une interface et recherche une exception par le type d'interface.

Interface:

public interface IMyException
{
    string MyProperty { get; }
}

Classe générique implémentant l'interface:

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

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

Classes dérivées:

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

    }
}

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

    }
}

Usage:

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

La même chose peut être faite en créant une classe de base qui hérite de Exception et en faisant MyException<T> dérivent de cette classe de base.

15
Eliahu Aaron

Le test si votre Type est dérivé d'un générique est:

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

Mais cela n'est vrai que pour les types dérivés eux-mêmes, comme MyException<int> ou MyException<string>.

Si vous avez d'autres dérivés comme MyDerivedStringException, vous avez dû tester sur:

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

Cela fonctionne donc pour tout générique existant, mais vous devez connaître le niveau d'héritage pour ce test, ou parcourir tous les types de base.

Vous pouvez donc faire ceci:

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

Cette implémentation met en boîte et décompresse si T: Value est Valuetype ... mais en ce qui concerne l'utilisation, vous pouvez contrôler les implications en termes de performances avec le nombre de fois que vous tentez de décompresser/décompresser.

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

Logique

try {
     ...
} catch(MyException e) {
    ...
}
3
Trae Moore

Malheureusement, vous ne pouvez pas attraper avec le

catch(MyException ex) classe, car elle attend un type.

Votre meilleur pari serait de créer une classe ou une interface de base, de l'attraper et d'en obtenir le type.

Il y a déjà une réponse sur alors ici sur la façon d'obtenir le type et de le faire.

2
Athanasios Kataras

cette implémentation résume le délégué de gestion pour le type d'exception loin du contexte du T/C ... Cela peut être un peu trop aventureux, mais la partie intéressante à ce sujet est que vous pouvez injecter différents gestionnaires en fonction de la portée de la réutilisation et du contexte de utilisation.

public interface IExceptionHandler
{
    object Handle(Exception ex);

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

logique d'enregistrement

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

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