Cara menangkap semua varian pengecualian umum di C #

22

Saya ingin menangkap semua varian dari kelas pengecualian umum dan saya bertanya-tanya apakah ada cara untuk melakukannya tanpa beberapa blok penangkap. Sebagai contoh katakanlah saya memiliki kelas pengecualian:

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

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

dan dua kelas turunan:

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

    }
}

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

    }
}

apakah ada cara menangkap keduanya MyDerivedStringExceptiondan MyDerivedIntExceptiondalam satu blok tangkapan.

Saya sudah mencoba ini:

try
{
   ...
}

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

tetapi tidak sangat bersih dan berarti saya tidak memiliki akses ke MyProperty.

Saya tertarik pada solusi umum untuk masalah ini, tetapi dalam kasus saya, Pengecualian generik didefinisikan di perpustakaan pihak ketiga yang sebagaimana ditunjukkan di bawah ini menambahkan beberapa kendala tambahan untuk masalah tersebut.

SBFrancies
sumber

Jawaban:

15

Buat MyException<T>implementasikan antarmuka dan periksa pengecualian dengan tipe antarmuka.

Antarmuka:

public interface IMyException
{
    string MyProperty { get; }
}

Kelas generik yang mengimplementasikan antarmuka:

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

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

Kelas turunan:

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

    }
}

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

    }
}

Pemakaian:

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

Hal yang sama dapat dilakukan dengan membuat kelas dasar yang mewarisi dari Exceptiondan dari membuat MyException<T>berasal dari kelas dasar itu.

Eliahu Aaron
sumber
3
Saya tidak yakin itu layak antarmuka di sini - hanya kelas dasar non-generik antara Exceptiondan MyException<T>akan baik-baik saja.
Jon Skeet
Juga, OP masih tidak memiliki akses ke properti generik (tanpa menggunakan refleksi) yang saya kira adalah masalah sebenarnya.
DavidG
10

Tes jika Anda Typeberasal dari generik adalah:

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

Tetapi ini hanya berlaku untuk tipe turunan itu sendiri, seperti MyException<int>atau MyException<string>.

Jika Anda memiliki turunan lebih lanjut seperti MyDerivedStringExceptionAnda harus menguji pada:

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

Jadi ini berfungsi untuk semua generik yang ada, tetapi Anda perlu mengetahui tingkat pewarisan untuk tes ini, atau mengulangi semua tipe dasar.

Jadi Anda bisa melakukan ini:

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

Ini kotak implementasi dan unboxes jika T: Value is Valuetype ... tetapi sehubungan dengan pemanfaatan Anda dapat mengontrol implikasi kinerja dengan berapa kali Anda mencoba untuk 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;
    }
}

Logika

try {
     ...
} catch(MyException e) {
    ...
}
Trae Moore
sumber
Ini adalah jawaban yang bagus dan saya telah memutarnya - sayangnya itu tidak menyelesaikan masalah spesifik saya karena pengecualian umum ada di perpustakaan pihak ketiga sehingga saya tidak dapat mengeditnya.
SBFrancies
2

Sayangnya, Anda tidak dapat menangkap dengan

catch(MyException ex) kelas, karena mengharapkan suatu tipe.

Taruhan terbaik Anda adalah membuat kelas dasar atau antarmuka, menangkapnya dan mendapatkan jenisnya dari sana.

Sudah ada jawaban jadi di sini tentang cara mendapatkan jenis dan melakukan ini.

Athanasios Kataras
sumber
1

implementasi ini mengabstraksi delegasi penanganan untuk tipe pengecualian jauh dari konteks T / C ... Ini mungkin agak terlalu berani, tetapi bagian kerennya adalah Anda dapat menyuntikkan penangan yang berbeda tergantung pada cakupan penggunaan kembali dan konteks pemanfaatan.

public interface IExceptionHandler
{
    object Handle(Exception ex);

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

logika pendaftaran

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

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