2010-10-08 38 views
15

Tôi có một phương pháp như ...tuyên bố một phương pháp luôn ném một ngoại lệ?

int f() { 
    try { 
    int i = process(); 
    return i; 
    } catch(Exception ex) { 
    ThrowSpecificFault(ex); 
    } 
} 

này tạo ra một lỗi biên dịch "không phải tất cả đường dẫn mã trả về một giá trị". Nhưng trong trường hợp của tôi ThrowSpecificFault() sẽ luôn ném (ngoại lệ) thích hợp. Vì vậy, tôi buộc phải đặt một giá trị trả về ở cuối nhưng điều này là xấu xí. Mục đích của mẫu này ở vị trí đầu tiên là vì "process()" là một cuộc gọi đến dịch vụ web bên ngoài nhưng cần dịch nhiều ngoại lệ khác nhau để phù hợp với giao diện mong đợi của khách hàng (~ mẫu mặt tiền mà tôi giả sử) .

Bất kỳ cách nào khác để làm điều này?

+1

Liên quan: [Có thuộc tính chuẩn "không bao giờ trả về" cho các hàm C# không?] (Http://stackoverflow.com/questions/1999181/is-there-a-standard-never-returns-attribute-for-c -functions) –

Trả lời

47

tôi đề nghị bạn chuyển đổi ThrowSpecificFault(ex)-throw SpecificFault(ex); phương thức SpecificFault sẽ trả về đối tượng ngoại lệ được ném thay vì tự ném nó. Sạch hơn nhiều.

Đây là mẫu được đề xuất bởi Microsoft's guidelines (tìm văn bản "Sử dụng phương pháp trình tạo ngoại lệ").

+5

+1, đây là mẫu được đề xuất trong nguyên tắc của Microsoft. – Joe

+0

Bạn có liên kết không? – noctonura

+2

Tôi làm: http://msdn.microsoft.com/en-us/library/seyhszts.aspx; tìm văn bản "Sử dụng các phương thức trình tạo ngoại lệ". – CesarGon

3

No.

Hãy tưởng tượng nếu ThrowSpecificFault được định nghĩa trong một DLL riêng biệt. Nếu bạn sửa đổi các DLL để không ném một ngoại lệ, sau đó chạy chương trình của bạn mà không biên dịch lại nó, những gì sẽ xảy ra?

+3

Hãy tưởng tượng nếu phương thức Foo được định nghĩa trong một DLL riêng biệt.Nếu bạn sửa đổi Foo để trả về một dài thay vì một int, sau đó chạy chương trình của bạn mà gọi Foo mà không biên dịch lại nó, những gì sẽ xảy ra? *Chẳng có gì tốt đẹp cả*. Nó là * không bao giờ * chính xác để thay đổi chữ ký của một phương thức trong một thư viện bên ngoài và sau đó tiếp tục sử dụng nó mà không cần biên dịch lại. Đó là lý do tại sao chúng tôi có tem phiên bản, vv, trên các hội đồng. –

+0

@Eric - Tôi tin rằng tình hình giả định của SLaks không yêu cầu thay đổi chữ ký, vì vậy nó không rõ ràng là một sự thay đổi đột phá. – kvb

+2

@kvb: Tính năng được đề xuất, như tôi đã hiểu, là để nắm bắt thực tế rằng một phương pháp không bao giờ trả về * trong chữ ký của nó *. –

8

Vấn đề ở đây, là nếu bạn đi vào khối catch trong f(), hàm của bạn sẽ không bao giờ trả về giá trị. Điều này sẽ dẫn đến lỗi vì bạn đã khai báo hàm của mình là int có nghĩa là bạn đã nói với trình biên dịch rằng phương thức của bạn sẽ trả về một số nguyên.

Mã sau sẽ làm những gì bạn đang tìm kiếm và luôn trả về một số nguyên.

int f() { 
    int i = 0; 
    try { 
    i = process(); 

    } catch(Exception ex) { 
    ThrowSpecificFault(ex); 
    } 
    return i; 
} 

đặt câu trả lại vào cuối hàm của bạn và bạn sẽ ổn.

Luôn luôn là một ý tưởng tốt để đảm bảo phương pháp của bạn sẽ luôn trả về giá trị bất kể đường dẫn thực thi mà ứng dụng của bạn trải qua là gì.

+0

+1 bạn đã đánh bại tôi một phút !! –

1

Làm thế nào về:

int f() { 
int i = -1; 
try { 
    i = process();  
} catch(Exception ex) { 
    ThrowSpecificFault(ex); 
} 
return i; 
} 
+0

Tôi để lại câu trả lời này, nhưng Robert Greiner đã đánh tôi với nó. –

2

Bạn có ba lựa chọn:

Luôn mang tôi nhưng trước khi khai báo nó:

int f() { 
    int i = 0; // or some other meaningful default 
    try { 
     i = process(); 
    } catch(Exception ex) { 
     ThrowSpecificFault(ex); 
    } 
    return i; 
} 

Return ngoại trừ từ phương pháp này và vứt rằng:

int f() { 
    try { 
     int i = process(); 
     return i; 
    } catch(Exception ex) { 
     throw GenerateSpecificFaultException(ex); 
    } 
} 

Hoặc tạo một lớp Ngoại lệ tùy chỉnh và ném rằng:

int f() { 
    try { 
     int i = process(); 
     return i; 
    } catch(Exception ex) { 
     throw new SpecificFault(ex); 
    } 
} 
0

Có.

Đừng mong đợi ThrowSpecificFault() để ném ngoại lệ.Có nó trả lại ngoại lệ, sau đó ném nó ở đây.

Nó thực sự có ý nghĩa hơn nữa. Bạn không sử dụng ngoại lệ cho luồng 'bình thường', vì vậy nếu bạn ném một ngoại lệ mỗi lần, ngoại lệ sẽ trở thành quy tắc. Tạo ngoại lệ cụ thể trong chức năng, và ném nó ở đây vì nó là một ngoại lệ đối với dòng chảy ở đây ..

0

Tôi cho rằng bạn có thể làm cho ThrowSpecificFault trả về một đối tượng, và sau đó bạn có thể

return ThrowSpecificFault(ex)

Nếu không, bạn có thể viết lại ThrowSpecificFault như một hàm tạo cho một kiểu phụ Ngoại lệ, hoặc bạn chỉ có thể làm cho ThrowSpecificFault thành một nhà máy tạo ra ngoại lệ nhưng không ném nó.

3

Bạn có thể làm như thế này:

catch (Exception ex) 
{ 
    Exception e = CreateSpecificFault(ex); 
    throw e; 
} 
0

Trong trường hợp bạn nhưng đó là kiến ​​thức của bạn không phải là trình biên dịch. Bây giờ có cách để nói rằng phương pháp này chắc chắn sẽ ném một số ngoại lệ khó chịu.

Hãy thử điều này

int f() { 
    try { 
    return process(); 
    } catch(Exception ex) { 
    ThrowSpecificFault(ex); 
    } 
    return -1; 
} 

Bạn cũng có thể sử dụng ném chìa khóa từ

int f() { 
    try { 
    return process(); 
    } catch(Exception ex) { 
    throw ThrowSpecificFault(ex); 
    } 
} 

Nhưng sau đó phương pháp mà phải trả lại một số ngoại lệ thay vì ném nó.

0

Sử dụng Unity.Interception để dọn sạch mã. Với xử lý chặn, mã của bạn có thể trông như thế này:

int f() 
{ 
    // no need to try-catch any more, here or anywhere else... 
    int i = process(); 
    return i; 
} 


Tất cả bạn cần làm trong bước tiếp theo là xác định một handler đánh chặn, mà bạn có thể tùy chỉnh thiết kế riêng để xử lý ngoại lệ. Sử dụng trình xử lý này, bạn có thể xử lý tất cả các ngoại lệ được ném trong ứng dụng của mình. Nhược điểm là bạn không còn phải đánh dấu tất cả các mã của bạn với các khối try-catch.

public class MyCallHandler : ICallHandler, IDisposable 
{ 
    public IMethodReturn Invoke(IMethodInvocation input, 
     GetNextHandlerDelegate getNext) 
    { 
     // call the method 
     var methodReturn = getNext().Invoke(input, getNext); 

     // check if an exception was raised. 
     if (methodReturn.Exception != null) 
     { 
      // take the original exception and raise a new (correct) one... 
      CreateSpecificFault(methodReturn.Exception); 

      // set the original exception to null to avoid throwing yet another 
      // exception 
      methodReturn.Exception = null; 
     } 

     // complete the invoke... 
     return methodReturn; 
    } 
} 

Đăng ký lớp học cho trình xử lý có thể được thực hiện qua tệp cấu hình hoặc theo lập trình. Mã này khá đơn giản. Sau khi đăng ký, bạn nhanh chóng đối tượng bằng cách sử dụng Unity, như thế này:

var objectToUse = myUnityContainer.Resolve<MyObjectToUse>(); 

Thông tin thêm về Unity.Interception:

http://msdn.microsoft.com/en-us/library/ff646991.aspx

7

Ngay bây giờ một kiểu trả về có thể là một loại, hoặc "khoảng trống" có nghĩa là "không có kiểu trả về". Chúng tôi có thể trong lý thuyết thêm một loại trở lại đặc biệt thứ hai "không bao giờ", trong đó có ngữ nghĩa bạn muốn. Điểm kết thúc của câu lệnh biểu thức bao gồm lời gọi đến phương thức trả lại "không bao giờ" sẽ được coi là không thể truy cập được và vì vậy nó sẽ hợp pháp trong mọi ngữ cảnh trong C# trong đó "goto", "throw" hoặc "return" là hợp pháp .

Rất có khả năng điều này sẽ được thêm vào hệ thống kiểu bây giờ, sau 10 năm. Lần tới khi bạn thiết kế hệ thống kiểu từ đầu, hãy nhớ bao gồm loại "không bao giờ".

Các vấn đề liên quan