2010-09-29 41 views
5

Lưu ý: Tôi rất vui khi thông báo cho bạn rằng các bộ lọc ngoại lệ hiện có bằng ngôn ngữ C# 6.0.Bắt ngoại lệ với điều kiện

Đây là một thử nghiệm suy nghĩ, tôi quan tâm đến ý kiến ​​của bạn: Điều đó có hợp lý với bạn không? Bạn có biết liệu một cái gì đó tương tự đã được đề xuất cho ngôn ngữ lập trình C#? Tôi thậm chí sẽ không biết gửi đề xuất như vậy ở đâu ...

Ý tưởng là giới thiệu các yếu tố cú pháp để chỉ bắt ngoại lệ nếu nó đáp ứng một điều kiện nhất định.

Ví dụ về trường hợp sử dụng là khi làm việc với COM Interop: Mọi thứ luôn ném một số COMException. Mã lỗi phân biệt thực sự được chứa trong thông điệp của nó.

Vì vậy, những gì về (đề nghị 1):

try 
{ 
    ... 
} 
catch (COMException ex where ex.Message.Contains("0x800706BA")) 
{ 
    // RPC server unavailable 
} 
catch (COMException ex where ex.Message.Contains("0x80010001")) 
{ 
    // Call rejected by callee 
} 

mà dịch để:

try 
{ 
    ... 
} 
catch (COMException ex) 
{ 
    if (ex.Message.Contains("0x800706BA")) 
    { 
     // RPC server unavailable 
    } 
    else if (ex.Message.Contains("0x80010001")) 
    { 
     // Call rejected by callee 
    } 
    else 
    { 
     throw; 
    } 
} 

trường hợp tương tự là: SoapException, XmlException ...


kịch bản khác là khi ngoại lệ được bao bọc như ngoại lệ bên trong trong một ngoại lệ chung, và logic bắt nên phụ thuộc vào ngoại lệ bên trong.

Giả sử chúng tôi có API bao gồm các ngoại lệ như sau: catch (NumberFormatException ex) { throw new BusinessException(ex) }.

gì về (đề nghị 2A):

try 
{ 
    ... 
} 
catch (inner NumberFormatException nfex) 
{ 
    ... 
} 

mà dịch để:

catch (Exception ex where ex.InnerException is NumberFormatException) 
{ 
    NumberFormatException nfex = ex.InnerException; 
    ... 
} 

hay (2B):

catch (BusinessException bex inner NumberFormatException nfex) 
{ 
    ... 
} 

mà dịch để:

catch (BusinessException bex where bex.InnerException is NumberFormatException) 
{ 
    NumberFormatException nfex = bex.InnerException; 
    ... 
} 

Trong this case (originally from Java) nó có thể trông giống như (2C):

catch (RemoteAccessException raex inner inner MyException mex) 
{ 
    ... 
} 
+2

Thanh cao cho các tính năng ngôn ngữ mới trong C#. Và tính năng này không thêm gì mà bạn không thể làm dễ dàng theo ý kiến ​​của tôi. –

+0

@ 0xA3: Không, nó chỉ là cú pháp. Nhưng nó bằng cách nào đó ngoại suy những ý tưởng đằng sau LINQ (ít nhất là đề xuất 1). – chiccodoro

+0

Tôi nghĩ bạn có thể đăng nó lên Kết nối của Microsoft nơi bạn có thể yêu cầu tính năng này làm một tính năng. –

Trả lời

4

Exceptions và các loại có liên quan chặt chẽ. Nếu bạn muốn phân biệt giữa hai loại ngoại lệ riêng biệt, bạn nên tạo hai loại ngoại lệ. Trong ví dụ của bạn, bạn sẽ có một Com800706BAException và Com80010001Exception.

Bây giờ, điều này không phải lúc nào cũng có thể hoặc khả thi, ví dụ nếu hệ thống cơ bản sử dụng mã lỗi thay vì ngoại lệ. Trong trường hợp đó, phương pháp của bạn có thể hữu ích. Tuy nhiên, tính năng ngôn ngữ này sẽ dễ bị lạm dụng. Ví dụ, bạn có thể làm xử lý lỗi như thế này, mà không phải là loại an toàn:

catch (Exception e where e.Message = "The foo barfed up the bar") 

Nếu bạn muốn kiểm tra ngoại lệ bên trong của một ngoại lệ, bạn đang làm việc xử lý theo mức độ sai lỗi.Ý tưởng là một phương pháp ném một ngoại lệ tổng quát để trừu tượng người gọi từ bên trong làm việc của phương pháp. Nếu bạn phụ thuộc vào một số ngoại lệ bên trong, bạn được kết hợp chặt chẽ với việc thực hiện phương pháp. Thật tệ.

Có thể ném ngoại lệ chung, riêng biệt hoặc xử lý lỗi bên trong phương thức.

+0

Như bạn nói, * điều này không phải lúc nào cũng có thể *: Interop luôn ném 'COMException'. Tôi là khách hàng của Interop, không phải là lập trình viên ... – chiccodoro

8

VB.Net có tính năng này của bộ lọc ngoại lệ như hình dưới đây

Catch ex As COMException When ex.ErrorCode = 0x800706BA 

Vì vậy, đây được hỗ trợ bởi CLR nhưng tính năng không được tiếp xúc trong C#

Giả sử F # có tính năng này cũng nhưng Tôi không biết nhiều về F # để hiển thị ví dụ.

+0

+1 Tuyệt vời! Kể từ VB.NET có tính năng này, tôi không nghĩ rằng đó là một công việc khó khăn cho nhóm dev để thêm tính năng này vào C#. –

+0

Nhưng sau đó có lẽ không phải là một yêu cầu không rõ, và như những người khác đã nói, nó không cung cấp cho bạn sức mạnh để làm bất cứ điều gì bạn không thể hiện đang làm. Nói thẳng ra, nó thậm chí còn không đơn giản hóa nhiều thứ. Như vậy, chi phí thêm nó (và các manh mối hoặc phút để thay đổi cú pháp chỉ là một phần nhỏ của nó, google cho Eric Lippert và có thể bạn sẽ tìm thấy một số danh sách những thứ có giá trong một thay đổi) có lẽ quá lớn cho những gì cơ bản số tiền cú pháp khác nhau để làm những gì bạn đã có thể làm. Vì vậy, không, có lẽ không khó, nhưng có lẽ cũng sẽ không được thực hiện. –

+0

Đường cú pháp, điều này không được hỗ trợ ở cấp CLR. – leppie

2

Tại sao lại nướng thứ gì đó vào ngôn ngữ không quan trọng?

Đề xuất 1 có thể dễ dàng giải quyết bằng câu lệnh chuyển đổi nhắm mục tiêu bên trong sản phẩm khai thác - theo cách đó bạn có thể xử lý ngoại lệ COM mà bạn muốn và chỉ cần làm lại mọi thứ bạn không muốn xử lý.

Vấn đề với Proposal 2 là ngoại lệ stack có thể tùy ý sâu, và có thể (hoặc sẽ) chứa:

  • nhiều trường hợp lồng nhau của cùng một loại ngoại lệ - cái nào nên bạn cú pháp truy vấn đối phó với?

  • ngoại lệ khác nhau xuất phát từ cùng một ngoại lệ cơ bản * - nếu cú ​​pháp truy vấn của bạn chỉ định một trong các ngoại lệ cơ sở cấp thấp hơn, điều đó có thể khớp với một loạt các ngoại lệ cấp cao hơn trong ngăn xếp, bạn là ai tìm cách để xử lý?

Hầu hết thời gian khi bạn đang lặp lại chồng ngoại lệ, bạn sẽ không muốn truy xuất ngoại lệ nằm ở nửa chừng ngăn xếp, thay vào đó bạn sẽ đi bộ ngăn xếp ngoại lệ và nhận bản ghi đầu tiên mục đích. Phần còn lại của thời gian bạn chỉ quan tâm đến ngoại lệ cuối cùng (bên ngoài). Cá nhân tôi không thể nhớ lại thời điểm mà tôi có nhu cầu lập trình bắt/xử lý ngoại lệ được chôn ở đâu đó ở giữa ngăn xếp, nó luôn là lần đầu tiên, cuối cùng hoặc tất cả chúng.

* bất chấp thực tế là tất cả các trường hợp ngoại lệ bắt nguồn từ System.Exception - tôi đã có nghĩa là hơn dọc theo dòng của MyBaseException đó tất cả các trường hợp ngoại lệ tùy chỉnh của bạn xuất phát từ đâu.

1

Tôi không chắc mình thích nó nhiều đến thế. Đầu tiên, nó nghe như một ý tưởng thực sự gọn gàng, nhưng sau đó tôi nghĩ, nếu thêm đường cú pháp cho loại điều này, mọi người sẽ có khả năng lạm dụng ngoại lệ khi mã trạng thái phù hợp hơn.

Như một vài người đã chỉ ra, đây là đã có trong VB, tuy nhiên bạn có thể dễ dàng làm điều gì đó tương tự như trong C#:

catch (Exception ex) 
{ 
    if (ex.Message.Contains("Yikes!")) 
    { 
    // Do your thing 
    } 
    ... 
    else 
    { 
    throw; 
    } 
} 

Vì vậy, nó là đường thực sự chỉ là cú pháp.

Điều về ngoại lệ là (thường được thảo luận trên trang web này) chúng vi phạm cấu trúc tuần tự của chương trình, bằng cách bỏ qua rất nhiều mã và ngăn xếp khi bạn thực sự không muốn. Đây là lý do tại sao tôi không nghĩ rằng họ là tốt cho bất cứ điều gì nhưng điều kiện rất đặc biệt, và nếu bạn thực sự có thể bằng cách nào đó xử lý các điều kiện này, nó sẽ là một phần của một cơn đau (try {} catch {if (..)} vv .), để mọi người sẽ không thể sử dụng ngoại lệ cho nhiều hơn những điều kiện đặc biệt này.

2

Tôi nghĩ rằng nó khá phức tạp và khó hiểu:

Điều gì xảy ra nếu điều kiện ngoại lệ của bạn cũng có ngoại lệ? Trường hợp ngoại lệ mới nên được xử lý ở đâu và như thế nào?

  • Thử/bắt xung quanh khối catch? meh ..
  • Hãy catch khối khác xử lý ngoại lệ rằng: StackOverflows Yay :)
+0

Hết bộ nhớ ngoại lệ -> cố gắng định dạng một chuỗi để in ra bàn điều khiển -> Hết bộ nhớ ngoại lệ -> ngăn xếp tràn ... – James

13

Theo try-catch C# Reference for Visual Studio 2015 RC này hiện đang thực hiện:

Catch (ArgumentException e) when (e.ParamName == "…") 
{ 
} 
+2

Chúng thay đổi nếu bắt (Ngoại lệ e) khi (e.ParamName == " ...) –

+1

Đây phải là câu trả lời được chấp nhận –

0

Kể từ C# 7.0 loại công cụ này đã được hỗ trợ. Here là tham chiếu đầy đủ. Đây là đoạn mã.

try 
    { 
     SomeOperationThatThrowsException(); 
    } 
    catch(TheFilteredException ex) when (ex.ErrorCode==123) 
    { 
     DoSomethingWhenExceptionThrown(); 
    } 
Các vấn đề liên quan