Tại sao có một phương thức trả về bool / int và có đối tượng thực tế làm tham số đầu ra?


12

Tôi thấy mẫu mã sau đây xuất hiện ở khắp nơi trong cơ sở mã của công ty tôi (ứng dụng .NET 3.5):

bool Foo(int barID, out Baz bazObject) { 
    try { 
            // do stuff
            bazObject = someResponseObject;

            return true;
    }
    catch (Exception ex) { 
        // log error
        return false;
    }
}

// calling code
BazObject baz = new BazObject();
fooObject.Foo(barID, out baz);

if (baz != null) { 
    // do stuff with baz
}

Tôi đang cố gắng che giấu lý do tại sao bạn sẽ làm điều này thay vì Foophương thức chỉ cần lấy ID và trả về một Bazđối tượng, thay vì trả về một giá trị không được sử dụng và có đối tượng thực tế là tham số đầu ra hoặc tham số đầu ra.

Có một số lợi ích tiềm ẩn của phong cách mã hóa này mà tôi đang thiếu?



Trong ví dụ của bạn, baztrở nullvà trở boolcon người falsekhông tương đương. new BazObject()là không bao giờ null, vì vậy trừ khi bazObjectđược cập nhật trước khi Exceptionném vào Foo, khi falseđược trả lại bazsẽ không bao giờ null. Nó sẽ giúp rất nhiều nếu các đặc tả cho Foođã có sẵn. Trong thực tế, đây có lẽ là vấn đề nghiêm trọng nhất được thể hiện bởi mã này.
Steve Powell

Trí nhớ của tôi mơ hồ vì điều này đã lâu rồi nhưng tôi nghĩ rằng tôi đã làm hỏng ví dụ và nó đang kiểm tra xem "baz" có sai không, nếu nó không có giá trị. Trong mọi trường hợp, mô hình dường như thực sự cổ xưa đối với tôi, giống như từ VB6 và nhà phát triển không bao giờ bận tâm cải thiện mã của mình (điều mà anh ta đã không làm)
Wayne Molina

Câu trả lời:


11

Bạn thường sử dụng mẫu đó để bạn có thể viết mã như thế này:

if (Foo(barId, out bazObject))
{
  //DoStuff with bazobject
}

ví dụ, nó được sử dụng trong CLR cho TryGetValue trong lớp từ điển. Nó tránh được một số dư thừa nhưng các tham số out và ref luôn có vẻ hơi lộn xộn với tôi


1
+1, đây là lý do tại sao, mặc dù tôi có xu hướng trả lại một đối tượng bằng cả boolean và đối tượng thay vì trả lại cả hai một cách riêng biệt
pdr

Sẽ đánh dấu đây là câu trả lời vì nó giải thích lý do, mặc dù sự đồng thuận dường như khá lỗi thời trừ những trường hợp cụ thể.
Wayne Molina

Câu trả lời này biện minh cho việc sử dụng mẫu này. Nhưng nếu đó là TẤT CẢ CÁC cơ sở mã của bạn, thì đó có lẽ là một thực tiễn tồi tệ như Sean.
Codism

11

Đây là mã kiểu C cũ, trước khi có ngoại lệ. Giá trị trả về cho biết phương thức có thành công hay không và nếu có, tham số sẽ được điền với kết quả.

Trong .net, chúng tôi có ngoại lệ cho mục đích đó. Không nên có lý do để theo mô hình này.

[sửa] Rõ ràng có ý nghĩa về hiệu suất trong xử lý ngoại lệ. Có thể là có cái gì đó để làm với nó. Tuy nhiên, trong đoạn mã đó đã có một ngoại lệ được đưa ra. Sẽ tốt hơn nếu chỉ để nó di chuyển lên ngăn xếp cho đến khi nó bị bắt ở một nơi thích hợp hơn.


Không thể đồng ý với điều đó. Không bao giờ sử dụng một ngoại lệ cho một điều kiện dự kiến. Ví dụ, nếu bạn đang phân tích một chuỗi thành một int, thì luôn có khả năng ai đó sẽ vượt qua một chuỗi không thể mã hóa. Bạn không nên ném ngoại lệ trong trường hợp đó
pdr

Đó không phải là những gì tôi nói. Nếu bạn đọc mã mà OP đã đăng, rõ ràng có một trường hợp ngoại lệ xảy ra, nhưng phương thức bắt nó và trả về sai thay thế. Câu hỏi này liên quan đến xử lý lỗi, điều kiện không mong đợi.
Sean Edwards

Vâng, nó dường như chủ yếu được sử dụng trong các phương thức tải dữ liệu từ cơ sở dữ liệu, if (baz.Select())nhưng thường xuyên hơn không phải là giá trị trả về bị vứt đi và giá trị được kiểm tra đối với null hoặc một số thuộc tính.
Wayne Molina

@Sean, hãy xem những gì bạn đang nói, tôi chỉ phản đối cụm từ "Trong .NET chúng tôi có ngoại lệ cho mục đích đó." Các ngoại lệ phục vụ một mục đích hoàn toàn khác đối với tôi
pdr

1
Ok, hãy để tôi nói rõ. Đúng là bạn không nên thêm boolean vào mọi phương thức để tính toán các lỗi mã hóa trên các đối số không hợp lệ. Nhưng trong trường hợp đầu vào của một phương thức đến từ người dùng chứ không phải nhà phát triển, bạn sẽ có thể cố gắng xử lý đầu vào mà không ném ngoại lệ nếu họ hiểu sai.
pdr

2

Với đoạn mã, nó trông hoàn toàn vô nghĩa. Đối với tôi, mẫu mã ban đầu sẽ gợi ý rằng null cho BazObject sẽ là một trường hợp có thể chấp nhận được và trả về bool là một biện pháp xác định trường hợp thất bại một cách an toàn. Nếu đoạn mã sau là:

// calling code
BazObject baz = new BazObject();
bool result = fooObject.Foo(barID, out baz);

if (result) { 
    // do stuff with baz
    // where baz may be 
    // null without a 
    // thrown exception
}

Điều này sẽ có ý nghĩa hơn với tôi để làm theo cách này. Có lẽ đây là một phương thức mà ai đó trước khi bạn sử dụng để đảm bảo rằng baz được truyền qua tham chiếu mà không hiểu làm thế nào các tham số đối tượng thực sự hoạt động trong C #.


Tôi nghĩ rằng nó đã được viết bởi một thành viên hiện tại của đội. Có lẽ đó là điều tôi nên lo lắng về O_O
Wayne Molina

@Wayne M: Ít lo lắng hơn một cơ hội đào tạo tiềm năng :)
Joel Etherton

1

Đôi khi mẫu này hữu ích khi bạn, người gọi, không cần quan tâm xem loại trả về là loại tham chiếu hay loại giá trị. Nếu bạn đang gọi một phương thức như vậy để truy xuất một loại giá trị, loại giá trị đó sẽ cần một giá trị không hợp lệ được thiết lập (ví dụ: double.NaN) hoặc bạn cần một số cách khác để xác định thành công.


Điều đó có ý nghĩa. Có vẻ hơi lạ, nhưng đó có vẻ là một lý do hợp lệ.
Wayne Molina

0

Ý tưởng là trả về một giá trị cho biết liệu quy trình có thành công hay không, cho phép mã như thế này:

Baz b;
if (fooObject.foo(id, out b)) {
   // do something with b
}
else {
   Error.screamAndRun("object lookup/whatever failed! AAaAAAH!");
}

Nếu đối tượng không thể là null (có vẻ đúng trong ví dụ của bạn), thì tốt hơn nên làm như sau:

Baz b = fooObject.foo(id);
if (b != null) {
   // do something with b
}
else {
   Error.screamAndRun("object lookup/whatever failed! AAaAAAH!");
}

Nếu đối tượng có thể là null, ngoại lệ là cách để đi:

try {
   Baz b = fooObject.foo(id);
}
catch (BazException e) {
   Error.screamAndRun("object lookup/whatever failed! AAaAAAH!");
}

Đó là một mô hình phổ biến được sử dụng trong C, nơi không có ngoại lệ. Nó cho phép hàm trả về một điều kiện lỗi. Ngoại lệ nói chung là một giải pháp sạch hơn nhiều.


Đặc biệt vì mã đã ném một ngoại lệ.
David Thornley
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.