Jak złapać wszystkie warianty ogólnego wyjątku w C #

22

Chciałbym wyłapać wszystkie warianty ogólnej klasy wyjątków i zastanawiałem się, czy można to zrobić bez wielu bloków wyłapywania. Powiedzmy na przykład, że mam klasę wyjątków:

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

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

oraz dwie klasy pochodne:

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

    }
}

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

    }
}

jest sposób na złapanie obu MyDerivedStringExceptioni MyDerivedIntExceptionjednego bloku połowu.

Próbowałem tego:

try
{
   ...
}

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

ale nie jest bardzo czysty i oznacza, że ​​nie mam dostępu MyProperty.

Interesuje mnie ogólne rozwiązanie problemu, ale w moim przypadku ogólny wyjątek jest zdefiniowany w bibliotece strony trzeciej, która, jak wskazano poniżej, wprowadza dodatkowe ograniczenia dla problemu.

SBFrancies
źródło

Odpowiedzi:

15

Utwórz MyException<T>interfejs i sprawdź wyjątek według typu interfejsu.

Berło:

public interface IMyException
{
    string MyProperty { get; }
}

Klasa ogólna implementująca interfejs:

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

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

Klasy pochodne:

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

    }
}

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

    }
}

Stosowanie:

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

To samo można zrobić, tworząc klasę bazową, która dziedziczy po tej klasie bazowej, Exceptioni niż ją MyException<T>wywodzi.

Eliahu Aaron
źródło
3
Nie jestem pewien, czy warto tutaj interfejs - po prostu nie-ogólna klasa bazowa pomiędzy Exceptioni MyException<T>byłaby w porządku.
Jon Skeet
Ponadto OP nadal nie ma dostępu do właściwości ogólnej (bez użycia odbicia), co, jak sądzę, jest prawdziwym problemem.
DavidG
10

Test, jeśli twoja Typepochodna pochodzi od ogólnej, to:

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

Ale dotyczy to tylko samych typów pochodnych, takich jak MyException<int>lub MyException<string>.

Jeśli masz inne pochodne, takie jak MyDerivedStringExceptionmusiałeś przetestować:

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

Działa to w przypadku każdego istniejącego rodzaju ogólnego, ale musisz znać poziom dziedziczenia dla tego testu lub przejść przez wszystkie typy podstawowe.

Więc możesz to zrobić:

catch(Exception ex) when (ex.GetType.BaseType.GetGenericTypeDefinition()==typeof(MyException<>))
Holger
źródło
3

Ta implementacja wykonuje i rozpakowuje, jeśli T: Wartość to Typ wartości ... ale w odniesieniu do wykorzystania można kontrolować wpływ na wydajność za pomocą liczby prób uruchomienia / rozpakowania.

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

Logika

try {
     ...
} catch(MyException e) {
    ...
}
Trae Moore
źródło
To dobra odpowiedź i głosowałem - niestety to nie rozwiązuje mojego konkretnego problemu, ponieważ ogólny wyjątek znajduje się w bibliotece innej firmy, więc nie mogę go edytować.
SBFrancies
2

Niestety nie możesz złapać

catch(MyException ex) klasa, ponieważ oczekuje typu.

Najlepszym rozwiązaniem byłoby utworzenie klasy podstawowej lub interfejsu, złapanie go i stamtąd typ.

Jest już odpowiedź na tak oto , w jaki sposób uzyskać typ i to zrobić.

Athanasios Kataras
źródło
1

ta implementacja oddziela delegata obsługi dla typu wyjątku od kontekstu T / C ... Może to być trochę zbyt ryzykowne, ale fajne jest to, że możesz wstrzykiwać różne procedury obsługi w zależności od zakresu ponownego użycia i kontekstu wykorzystanie.

public interface IExceptionHandler
{
    object Handle(Exception ex);

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

logika rejestracji

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

try {
    ...
}catch(Exception ex)
{
    ...any validation
    return exceptionHandler.Handle(ex)
}
Trae Moore
źródło