Cố ý nâng cao ngoại lệ để sử dụng bắt


10

Đối với một if...elsegói được xử lý ngoại lệ điển hình , có phải ví dụ như ví dụ sau đây là một thực tiễn được đề xuất để tránh trùng lặp mã không?

try
{
    if (GetDataFromServer())
    {
        return ProcessData();
    }
    else
    {
        throw new Exception();
    }
catch(Exception ex)
{
    return null;
}

thay vì...

try
{
    if (GetDataFromServer())
    {
        return ProcessData();
    }
    else
    {
        return null;
    }
}
catch(Exception ex)
{
    return null;
}

Tôi biết có một hiệu suất nhẹ, nhưng tôi tự hỏi nếu điều này được coi là một thực hành chấp nhận được. Tôi hiện đang thực hiện phương pháp thứ hai - đặc biệt trong các trường hợp tôi cần xử lý các trường hợp ngoại lệ cụ thể khác nhau - nhưng tôi đã tự hỏi liệu phương pháp đầu tiên có phù hợp với các trường hợp đơn giản không.


nếu phương thức này đủ nhỏ, tôi sẽ loại bỏ cái khác và trả về null bên ngoài khối trycatch để tôi chỉ phải trả về null một lần.
Fabio Marcolini

Câu trả lời:


12

Việc sử dụng xử lý ngoại lệ để kiểm soát luồng được Microsoft khuyến khích.

Và một bàn tròn về chủ đề có sẵn.

Điều đó đang được nói, C # hỗ trợ làm như vậy và tôi cho rằng nó phụ thuộc vào điều kiện gặp phải cho dù một ngoại lệ là phản ứng thích hợp nhất.


1
Tôi nhận ra rằng loại điều này chỉ đang cố gắng thực sự để không sử dụng các sự kiện.
radarbob

@radarbob: Làm thế nào là sự kiện liên quan đến điều này?

@grovesNL - Ném một ngoại lệ vào một điểm cụ thể để gọi một phương thức nhất định trong khối bắt? Quacks như một sự kiện với tôi.
radarbob

@radarbob: Đây không phải là một sự kiện. Có rất nhiều trường hợp sử dụng mẫu trong đó điều này sẽ được sử dụng, như được thảo luận trong liên kết bàn tròn của câu trả lời.

1
@radarbob Chỉ cần làm rõ một chút, các ngoại lệ được thiết kế như một cách báo hiệu cho người gọi rằng có một cái gì đó đã xảy ra mà phương thức được gọi là không thể xử lý. Tuy nhiên, một sự kiện phải được lắng nghe. Một ngoại lệ là sự gián đoạn bắt buộc của dòng chảy bình thường của một chương trình. Một ngoại lệ chưa được lưu sẽ khiến toàn bộ ứng dụng phải sử dụng.

6

Các hit hiệu suất có lẽ là không đáng kể, như được giải thích trong câu trả lời này .

Vì vậy, hãy đi với ý tưởng rằng hiệu suất không phải là một vấn đề. Bạn đang ném System.Exception, chỉ để di chuyển thực hiện vào catchmệnh đề . Ném một cái BadControlFlowThatShouldBeRewrittenExceptioncó lẽ sẽ là quá mức cần thiết mặc dù.

Hãy phá vỡ điều này. Chúng ta có:

  • Phương thức GetDataFromServer(tên phương thức phải là PascalCase trong C #), có thể có thể ném ngoại lệ hoặc trả về a bool.
  • Nếu kết quả là true, chạy ProcessData.
  • Trả lại nullnếu không.

Có vẻ như phương thức mà mã này được viết, chỉ đơn giản là làm quá nhiều thứ. GetDataFromServertrả về một giao booldiện trông giống như một lỗi thiết kế, tôi đang mong đợi phương thức đó trả về dữ liệu mà nó nhận được từ máy chủ , một số IEnumerable<SomeType>có chứa 0 hoặc nhiều mục - tức là đường dẫn hạnh phúc trả về n mục trong đó n> 0 , không hạnh phúc đường dẫn trả về 0 mục và đường dẫn không vui sẽ xuất hiện với một ngoại lệ chưa được xử lý, bất kể đó là gì.

Điều đó thay đổi phương thức trông như thế nào, khá nhiều - một lần nữa thật khó để biết liệu điều này có hợp lý hay không, bởi vì bài đăng gốc chỉ có một điểm thoát (và do đó sẽ không biên dịch, vì không phải tất cả các đường dẫn mã đều trả về một giá trị ), vì vậy Đây chỉ là một phỏng đoán hoang dã:

try
{
    var result = GetDataFromServer();
    return ProcessData(result);
}
catch
{
    return null;
}

Ở đây bạn nhìn vào ProcessDatavà thấy rằng nó đang lặp lại resultvà trả về nullnếu không có mục nào trong IEnumerable.

Bây giờ tại sao phương thức trở lại null? Máy chủ bị sập? Có một lỗi trong truy vấn? Chuỗi kết nối đang sử dụng thông tin sai? Bất cứ khi nào GetDataFromServernổ tung với một ngoại lệ mà bạn không mong đợi, bạn sẽ nuốt nó, đẩy nó xuống dưới thảm và trả lại một nullgiá trị. Tôi khuyên bạn nên nắm bắt các ngoại lệ cụ thể trong trường hợp này và ghi nhật ký mọi thứ khác; cách gỡ lỗi sẽ dễ dàng hơn nhiều theo cách đó.

Với một catchmệnh đề chung không nắm bắt được ngoại lệ, sẽ rất khó để chẩn đoán bất cứ điều gì. Thay vào đó tôi sẽ làm điều này tối thiểu:

catch(Exception e)
{
    return null;
}

Bây giờ bạn ít nhất có thể phá vỡ và kiểm tra enếu mọi thứ đi sai.


TL; DR : Không, ném và bắt ngoại lệ cho kiểm soát dòng chảy không phải là một ý tưởng tốt.


Câu trả lời này cho thấy chính xác lý do tại sao tôi cố gắng giữ mã chung của mình: Tôi không muốn liệt kê ra mọi ngoại lệ mà tôi thực sự tạo ra trong mã của mình; Tôi không muốn liệt kê tên phương thức thực tế; Tôi không muốn liệt kê khai báo phương thức; Tôi không muốn đề xuất cú pháp. Tôi đã có một câu hỏi duy nhất về việc liệu ném ngoại lệ cho kiểm soát dòng chảy có ổn không, đã được B2K trả lời kịp thời. Tôi sẽ rất vui khi tranh luận về điều này trên meta.

3
Nghe có vẻ như đây là một câu hỏi cho các lập trình viên. chúng tôi xem xét mã, không phải ý tưởng.
Malachi

2

trong câu trả lời đầu tiên của bạn có một hit hiệu suất không cần phải ở đó.

try
{
    if (GetDataFromServer())
    {
        return ProcessData();
    }
    else
    {
        throw new Exception();
    }
catch(Exception ex)
{
    return null;
}

khi bạn thoát câu lệnh if để nhập vào câu lệnh Catch khi bạn không phải thực hiện chuyển hướng mã, có thể nói như vậy.

nếu bạn muốn return null; làm điều đó trong câu lệnh khác không phải trong một lần bắt được sau khi bị ném từ câu lệnh khác.

Có thể không áp dụng cho mã Real của bạn , nhưng đối với mã Chung mà bạn đã cung cấp thì không áp dụng.

Các tiêu chuẩn nói rằng bạn không nên làm điều này.

Các tiêu chuẩn nói rằng bạn nên làm như thế này, (một lần nữa dựa trên Mã chung được đưa ra trong OP)

if (GetDataFromServer())
{
    return ProcessData();
}
else
{
    Return null
}

và vì bạn không có bất kỳ trường hợp ngoại lệ cụ thể nào mà bạn đang bắt, bạn thậm chí không nên thử bắt ở đây.

bạn muốn xem Ngoại lệ khi chúng xảy ra để bạn có thể khắc phục sự cố tạo ngoại lệ.


1

Tại sao không đơn giản hơn nhiều:

if (!GetDataFromServer()) return null;
ProcessData();

Nếu một trình xử lý ngoại lệ sẽ tồn tại thì nó phải nằm trong ProcessData ()


Tại sao tôi không muốn vượt qua ngoại lệ ProcessData()đến cấp cao nhất?
GrovesNL

@grovesNL Không có gì hữu ích được thực hiện với ngoại lệ ở đây.
Loren Pechtel

1
Làm sao vậy Nếu ProcessData()ném một ngoại lệ bây giờ nó không được xử lý. Tôi muốn nó return nullở cấp độ này nếu ProcessData()ném một ngoại lệ, mà không sửa đổi ProcessData()chính nó.
GrovesNL
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.