Cách bắt tất cả các biến thể của một ngoại lệ chung trong C #


22

Tôi muốn bắt tất cả các biến thể của một lớp ngoại lệ chung và tôi đã tự hỏi liệu có cách nào để làm điều đó mà không có nhiều khối bắt không. Ví dụ: tôi có một lớp ngoại lệ:

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

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

và hai lớp dẫn xuất:

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

    }
}

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

    }
}

có cách bắt cả hai MyDerivedStringExceptionMyDerivedIntExceptiontrong một khối bắt.

Tôi đã thử điều này:

try
{
   ...
}

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

nhưng nó không sạch sẽ lắm và có nghĩa là tôi không có quyền truy cập MyProperty.

Tôi quan tâm đến một giải pháp chung cho vấn đề nhưng trong trường hợp của tôi, Ngoại lệ chung được xác định trong thư viện bên thứ ba, như được chỉ ra dưới đây thêm một số ràng buộc bổ sung cho vấn đề.

Câu trả lời:


15

Thực MyException<T>hiện một giao diện và kiểm tra ngoại lệ theo loại giao diện.

Giao diện:

public interface IMyException
{
    string MyProperty { get; }
}

Lớp chung thực hiện giao diện:

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

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

Các lớp phái sinh:

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

    }
}

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

    }
}

Sử dụng:

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

Điều tương tự có thể được thực hiện bằng cách tạo một lớp cơ sở kế thừa từ Exceptionvà hơn là tạo MyException<T>ra từ lớp cơ sở đó.


3
Tôi không chắc chắn rằng nó có giá trị một giao diện ở đây - chỉ là một lớp cơ sở không chung chung giữa ExceptionMyException<T>sẽ ổn.
Jon Skeet

Ngoài ra, OP vẫn không có quyền truy cập vào thuộc tính chung (không sử dụng phản chiếu) mà tôi đoán là vấn đề thực sự.
DavidG

10

Bài kiểm tra nếu bạn Typecó nguồn gốc từ chung chung là:

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

Nhưng điều này chỉ đúng với các loại dẫn xuất, như MyException<int>hoặc MyException<string>.

Nếu bạn có thêm các dẫn xuất như MyDerivedStringExceptionbạn phải kiểm tra trên:

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

Vì vậy, điều này hoạt động cho bất kỳ chung chung hiện có, nhưng bạn cần biết mức độ kế thừa cho thử nghiệm này, hoặc lặp qua tất cả các loại cơ sở.

Vì vậy, bạn có thể làm điều này:

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

3

Hộp triển khai và hộp thư này nếu T: Giá trị là Valuetype ... nhưng đối với việc sử dụng, bạn có thể kiểm soát hàm ý hiệu suất với số lần bạn cố gắng để hộp / bỏ hộp.

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

Hợp lý

try {
     ...
} catch(MyException e) {
    ...
}

Đây là một câu trả lời hay và tôi đã nêu lên - thật không may, nó không giải quyết được vấn đề cụ thể của tôi vì ngoại lệ chung là trong thư viện của bên thứ ba nên tôi không thể chỉnh sửa nó.
SBFrancies

2

Không may mắn, bạn không thể bắt với

catch(MyException ex) lớp, như nó mong đợi một loại.

Đặt cược tốt nhất của bạn sẽ là tạo một lớp cơ sở hoặc giao diện, bắt nó và lấy kiểu từ đó.

Đã có một câu trả lời trên đây về cách lấy loại và làm điều này.


1

việc triển khai này trừu tượng hóa đại biểu xử lý cho loại ngoại lệ khỏi bối cảnh của T / C ... Điều này có thể hơi quá mạo hiểm, nhưng phần thú vị về nó là bạn có thể tiêm các trình xử lý khác nhau tùy thuộc vào phạm vi sử dụng lại và bối cảnh của sử dụng.

public interface IExceptionHandler
{
    object Handle(Exception ex);

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

logic đăng ký

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

try {
    ...
}catch(Exception ex)
{
    ...any validation
    return exceptionHandler.Handle(ex)
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.