Как отловить все варианты универсального исключения в C #

22

Я хотел бы поймать все варианты универсального класса исключений, и мне было интересно, есть ли способ сделать это без нескольких блоков catch. Например, скажем, у меня есть класс исключения:

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

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

и два производных класса:

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

    }
}

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

    }
}

Есть ли способ поймать MyDerivedStringExceptionи MyDerivedIntExceptionв одном и том же блоке?

Я пробовал это:

try
{
   ...
}

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

но это не очень чисто и означает, что у меня нет доступа к MyProperty.

Меня интересует общее решение проблемы, но в моем случае общее исключение определено в сторонней библиотеке, которая, как указано ниже, добавляет некоторые дополнительные ограничения к проблеме.

SBFrancies
источник

Ответы:

15

Сделайте MyException<T>реализацию интерфейса и проверьте исключение по типу интерфейса.

Интерфейс:

public interface IMyException
{
    string MyProperty { get; }
}

Общий класс, реализующий интерфейс:

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

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

Производные классы:

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

    }
}

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

    }
}

Применение:

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

То же самое можно сделать путем создания базового класса, который наследуется от этого базового класса, Exceptionа затем создает его MyException<T>.

Элиаху Аарон
источник
3
Я не уверен, что здесь стоит интерфейс - просто неуниверсальный базовый класс между ними Exceptionи MyException<T>все будет в порядке.
Джон Скит
Кроме того, OP по-прежнему не имеет доступа к универсальному свойству (без использования отражения), что, я думаю, является реальной проблемой.
DavidG
10

Тест, если ваш Typeполучен из универсального:

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

Но это верно только для самих производных типов, таких как MyException<int>или MyException<string>.

Если у вас есть дополнительные производные, как MyDerivedStringExceptionвы должны были проверить:

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

Так что это работает для любого существующего генерика, но вам нужно знать уровень наследования для этого теста или пройтись по всем базовым типам.

Так что вы можете сделать это:

catch(Exception ex) when (ex.GetType.BaseType.GetGenericTypeDefinition()==typeof(MyException<>))
Holger
источник
3

Эта реализация блокирует и распаковывает, если T: Value имеет значение Valuetype ... но в отношении использования вы можете контролировать влияние на производительность с количеством попыток блокировать / распаковывать.

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

логика

try {
     ...
} catch(MyException e) {
    ...
}
Тре Мур
источник
Это хороший ответ, и я проголосовал - к сожалению, он не решает мою конкретную проблему, поскольку общее исключение находится в сторонней библиотеке, поэтому я не могу его отредактировать.
SBFrancies
2

К сожалению, вы не можете поймать с

catch(MyException ex) класс, как и ожидается тип.

Лучше всего было бы создать базовый класс или интерфейс, поймать его и получить тип оттуда.

Существует уже ответ на так вот о том , как получить тип и сделать это.

Атанасиос Катарас
источник
1

эта реализация абстрагирует делегат обработки для типа исключения от контекста T / C ... Это может быть немного слишком авантюрно, но крутая часть в том, что вы можете вводить различные обработчики в зависимости от области повторного использования и контекста использование.

public interface IExceptionHandler
{
    object Handle(Exception ex);

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

логика регистрации

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

try {
    ...
}catch(Exception ex)
{
    ...any validation
    return exceptionHandler.Handle(ex)
}
Тре Мур
источник