2009-08-13 49 views
10

xem xét chữ ký phương pháp sau đây:Đây có phải là phong cách C# tốt không?

public static bool TryGetPolls(out List<Poll> polls, out string errorMessage) 

Phương pháp này thực hiện như sau:

  • truy cập cơ sở dữ liệu để tạo ra một danh sách các đối tượng Thăm dò ý kiến.
  • trả về true nếu thành công và lỗiMessage sẽ là một chuỗi trống
  • trả về false nếu nó không thành công và errorMessage sẽ chứa thông báo ngoại lệ.

Đây có phải là phong cách tốt không?

Cập nhật: phép nói rằng tôi làm sử dụng chữ ký phương pháp sau đây:

public static List<Poll> GetPolls() 

và trong phương pháp đó, nó không bắt bất kỳ trường hợp ngoại lệ (vì vậy tôi phụ thuộc người gọi để bắt ngoại lệ). Làm thế nào để tôi xử lý và đóng tất cả các đối tượng nằm trong phạm vi của phương pháp đó? Ngay sau khi một ngoại lệ được ném, mã đóng và hủy bỏ các đối tượng trong phương thức này không thể truy cập được nữa.

+1

Để định dạng mã, hãy thêm bốn dấu cách trước mã. Phương pháp đơn giản nhất là nhập/sao chép mã của bạn, đánh dấu tất cả, và nhấp vào nút "mã" trong trình chỉnh sửa. –

Trả lời

43

Đó phương pháp đang cố gắng để làm ba việc khác nhau:

  1. Lấy và trả về một danh sách các cuộc thăm dò
  2. Return một giá trị boolean chỉ ra thành công
  3. Return một thông báo lỗi

Đó là khá lộn xộn từ quan điểm thiết kế.

Một cách tiếp cận tốt hơn sẽ được tuyên bố đơn giản:

public static List<Poll> GetPolls() 

Sau đó để phương pháp này ném một Exception nếu bất cứ điều gì khó khăn.

+3

Có một TryGetPolls có thể hữu ích nếu nó có thể trả lại một cách hợp pháp không có gì. – Michael

+0

+1 cho giải pháp sạch –

+4

@Michael: phương pháp "Thử ..." rất hay trong các trường hợp cụ thể khi có sự cố xảy ra nhưng bạn không quan tâm (nhiều hay ít). Nếu nó có thể trả về không hợp lệ thì nó sẽ trả về null/Nothing. – STW

11

Tôi tin

public static bool TryGetPolls(out List<Poll> polls) 

sẽ thích hợp hơn. Nếu phương pháp là TryGet thì giả định ban đầu của tôi sẽ có lý do để mong đợi nó thất bại, và onus là người gọi để xác định phải làm gì tiếp theo. Nếu họ gọi là không xử lý lỗi, hoặc muốn thông tin lỗi, tôi mong họ gọi phương thức Get tương ứng.

+0

Thậm chí tốt hơn, hãy sử dụng IList để tăng khả năng trừu tượng hóa. –

7

Theo nguyên tắc chung, tôi sẽ nói không.

Lý do tôi nói không thực sự không phải vì bạn đang thực hiện một số TryGetX và trả về một bool với thông số out. Tôi nghĩ rằng đó là phong cách xấu bởi vì bạn cũng trả về một chuỗi lỗi.

Try chỉ nên bỏ qua một lỗi cụ thể, thường gặp phải. Các vấn đề khác vẫn có thể ném một ngoại lệ với thông báo ngoại lệ thích hợp. Hãy nhớ rằng mục tiêu của một phương pháp Try như thế này là để tránh chi phí của một ngoại lệ được ném khi bạn mong đợi một loại lỗi cụ thể duy nhất xảy ra thường xuyên hơn không.

Thay vào đó, những gì bạn đang tìm kiếm là một cặp phương pháp:

public static bool TryGetPolls(out List<Poll> polls); 
public static List<Poll> GetPolls(); 

Bằng cách này, người dùng có thể làm những gì là phù hợp và GetPolls có thể được thực hiện trong điều kiện của TryGetPolls. Tôi giả định rằng số static của bạn có ý nghĩa trong ngữ cảnh.

7

xem xét trở lại:

  • một bộ sưu tập trống
  • rỗng

Nhiều ra các thông số, với tôi, là một code smell. Phương pháp chỉ nên làm MỘT THỨ.

xem xét nâng cao và xử lý các thông báo lỗi với:

throw new Exception("Something bad happened"); 
//OR 
throw new SomethingBadHappenedException(); 
+1

+1: Đồng thời xem xét việc triển khai ngoại lệ tùy chỉnh cho trường hợp đã biết, trong trường hợp đó, nó sẽ bị lỗi do các điều kiện nhất định. –

+0

Nhưng bạn nên ném một cái gì đó cụ thể hơn một System.Exception mà phù hợp với loại lỗi chính xác hơn. –

+0

Cảm ơn Ian. Nó có nghĩa là nhiều hơn như là hướng dẫn rằng một ngoại lệ của một số loại nên được ném. Không phải là một bản sao/dán từ Bức tường phòng tắm. :) –

1

Nó phụ thuộc vào những gì được thông báo lỗi là. Ví dụ: nếu quá trình xử lý không thể tiếp tục vì kết nối cơ sở dữ liệu không có sẵn, v.v, thì bạn nên ném ngoại lệ như những người khác đã đề cập.

Tuy nhiên, có thể bạn chỉ muốn trả lại thông tin "meta" về lần thử, trong trường hợp đó bạn chỉ cần một cách để trả lại nhiều thông tin từ một cuộc gọi phương thức duy nhất. Trong trường hợp đó, tôi đề xuất tạo lớp PollResponse có chứa hai thuộc tính: Danh sách < Thăm dò ý kiến> Cuộc thăm dò và chuỗi LỗiMessage. Sau đó, phương pháp của bạn trả lại đối tượng PollResponse:

class PollResponse 
{ 
    public List<Poll> Polls { get; } 
    public string MetaInformation { get; } 
} 
+0

Đó là không thành ngữ như mã gốc, IMHO. Idiomatic C# sử dụng các ngoại lệ để báo cáo lỗi, không phải các đối tượng tùy chỉnh có thông báo lỗi trong chúng. –

+3

Kỹ thuật của Scott ở đây có thể tương tự như System.Web.HttpResponse Tôi không nghĩ rằng đó là giả định không công bằng khi xử lý một bộ sưu tập đối tượng Poll có thể đảm bảo một đối tượng hoàn toàn mới xử lý lỗi, ngôn ngữ, hết hạn, mức độ hiển thị của người dùng, v.v. –

+0

@ Greg - Tôi nghĩ rằng nó đi xuống đến những gì errorMessage thực sự là. Mọi người ở đây chỉ giả định rằng đó là một ngoại lệ của chương trình, nhưng đó không nhất thiết phải là trường hợp. Một ngoại lệ của chương trình có nghĩa là việc thực thi không thể tiếp tục. Một ngoại lệ và một lỗi không giống nhau. Khi người dùng cố gắng nhập một ký tự vào một trường số, đó là một lỗi, nhưng tôi không phải ném một ngoại lệ. Có thể trong trường hợp này, "lỗi" là dữ liệu Thăm dò ý kiến ​​là cũ hoặc có thể kết quả không thêm đến 100%. Tôi đã suy nghĩ nhiều hơn dọc theo dòng "làm thế nào để tôi trả lại hai thứ từ một cuộc gọi chức năng?" –

0

Phụ thuộc vào nếu lỗi xảy ra phổ biến hoặc nếu chúng tôi thực sự là ngoại lệ.

Nếu lỗi thực sự hiếm và xấu thì bạn có thể muốn xem xét việc có phương thức trả lại danh sách các cuộc thăm dò và ném ngoại lệ nếu xảy ra lỗi.

Nếu lỗi là một phần phổ biến thực tế của hoạt động bình thường, giống như lỗi khi cuộn chuỗi thành số nguyên trong phương thức int.TryParse, phương pháp bạn đã tạo sẽ phù hợp hơn.

Tôi đoán trước đây có lẽ là trường hợp tốt nhất cho bạn.

+0

Tôi đồng ý với tất cả các lý do của bạn ngoại trừ trong trường hợp thứ 2, tôi muốn sử dụng lời khuyên của yshuditelu, chứ không phải mã gốc. – Brian

+0

Tôi không nghĩ rằng "hiếm" là khái niệm đúng đắn. "Đặc biệt" chính xác hơn. Hãy xem xét một hàm chấp nhận một số dưới dạng chuỗi làm đối số. Nếu chuỗi không phải là một số, đó là một trường hợp đặc biệt, mặc dù không nhất thiết phải là một số hiếm. –

11

Đây chắc chắn không phải là cách viết thành ngữ C#, điều này cũng có nghĩa là nó có thể không phải là một phong cách hay.

Khi bạn có phương thức TryGetPolls thì điều đó có nghĩa là bạn muốn kết quả nếu hoạt động thành công và nếu không thì bạn không quan tâm tại sao nó không thành công.

Khi bạn chỉ đơn giản là phương thức GetPolls thì điều đó có nghĩa là bạn luôn muốn kết quả và nếu nó không thành công thì bạn muốn biết lý do tại sao dưới dạng Exception.

Trộn hai ở đâu đó ở giữa, điều này sẽ không bình thường đối với hầu hết mọi người. Vì vậy, tôi sẽ nói hoặc là không trả lại thông báo lỗi hoặc ném một số Exception về lỗi, nhưng không sử dụng phương pháp lai lẻ này.

Vì vậy, chữ ký phương pháp của bạn có lẽ nên được một trong hai:

IList<Poll> GetPolls(); 

hoặc

bool TryGetPolls(out IList<Poll> polls); 

(Lưu ý rằng tôi đang trả lại một IList<Poll> chứ không phải là một List<Poll> trong cả hai trường hợp quá, vì nó cũng tốt thực hành với chương trình trừu tượng hơn là triển khai.)

+0

Greg, đây là một điểm tuyệt vời. –

+0

+1 cho IList - Tôi cũng đã đề cập đến điều đó. –

+0

Tôi là tất cả cho trừu tượng trong một API công cộng đặc biệt nếu khách hàng không nằm trong tầm kiểm soát của bạn. Nhưng nếu đây là một lớp nội bộ có khả năng là bạn sẽ thay thế việc thực hiện Danh sách trong khuôn khổ .Net với cái gì khác. Nếu đó là một giao diện mà bạn có thể giả lập đủ công bằng, nhưng lớp .Net List? Làm thế nào về nguyên tắc KISS. – softveda

2

Không, theo quan điểm của tôi, đây là kiểu rất xấu. Tôi sẽ viết như sau:

public static List<Poll> GetPolls(); 

Nếu cuộc gọi không thành công, hãy ném ngoại lệ và đưa thông báo lỗi vào trường hợp ngoại lệ. Đó là những ngoại lệ và mã của bạn sẽ trở nên sạch hơn, dễ đọc hơn và dễ bảo trì hơn.

0

Tùy thuộc vào tần suất phương pháp sẽ thất bại. Nói chung, các lỗi trong. Net phải được liên lạc với một số Exception. Trường hợp quy tắc đó không giữ là khi lỗi thường xuyên xảy ra, và hiệu suất hoạt động của hàm số ing và ngoại lệ là throw quá cao.

Đối với loại công việc cơ sở dữ liệu Tôi nghĩ rằng Exception là tốt nhất.

0

Tôi muốn thiết lập lại nó như thế này.

public static List<Poll> GetPolls() 
{ 
    ... 
} 

Nó có lẽ nên được ném một ngoại lệ (các ErrorMessage) nếu nó không thành công để lấy các cuộc thăm dò, cộng này cho phép phương pháp chaining là ít cồng kềnh hơn đối phó với các thông số ra.

Nếu bạn chạy FxCop, bạn sẽ muốn thay đổi Danh sách thành IList để giữ cho nó hài lòng.

1

Không thực sự - tôi có thể thấy một số vấn đề với điều này.

Trước hết, phương pháp có vẻ như bạn thường mong đợi nó thành công; lỗi (không thể kết nối với cơ sở dữ liệu, không thể truy cập vào bảng polls, v.v.) sẽ rất hiếm. Trong trường hợp này, việc sử dụng ngoại lệ để báo cáo lỗi là hợp lý hơn nhiều. Mẫu Try... dành cho các trường hợp bạn thường kỳ vọng cuộc gọi "không thành công" - ví dụ: khi phân tích một chuỗi thành một số nguyên, rất có thể là chuỗi là đầu vào của người dùng có thể không hợp lệ, vì vậy bạn cần có cách nhanh để xử lý điều này - do đó TryParse. Đây không phải là trường hợp ở đây.

Thứ hai, bạn báo cáo lỗi dưới dạng giá trị bool cho biết sự hiện diện hoặc vắng mặt của lỗi và thông báo chuỗi. Người gọi sẽ phân biệt giữa các lỗi khác nhau như thế nào? Anh ấy chắc chắn không thể khớp với văn bản thông báo lỗi - đó là một chi tiết triển khai có thể thay đổi và có thể được bản địa hóa. Và có thể có một thế giới khác biệt giữa "Không thể kết nối với cơ sở dữ liệu" (có thể chỉ cần mở hộp thoại cài đặt kết nối cơ sở dữ liệu trong trường hợp này và cho phép người dùng chỉnh sửa?) Và "Đã kết nối với cơ sở dữ liệu, nhưng có nội dung 'Truy cập bị từ chối' ". API của bạn không có cách nào tốt để phân biệt giữa các API đó.

Để tổng hợp: sử dụng ngoại lệ thay vì bool + out string để báo cáo tin nhắn. Khi bạn làm điều đó, bạn chỉ có thể sử dụng List<Poll> làm giá trị trả về, không cần đối số out. Và, tất nhiên, đổi tên phương thức thành GetPolls, vì Try... được dành riêng cho mẫu bool+out.

0

Tôi nghĩ nó ổn. Tôi muốn mặc dù:

enum FailureReasons {} 
public static IEnumerable<Poll> TryGetPolls(out FailureReasons reason) 

Vì vậy, các chuỗi lỗi không sống trong các mã truy cập dữ liệu ... Phương pháp

0

C# nên thực sự chỉ làm một việc. Bạn đang cố gắng làm ba việc với phương pháp đó. Tôi sẽ làm như những người khác đã đề nghị và ném một ngoại lệ nếu có một lỗi. Một tùy chọn khác là tạo các phương thức mở rộng cho đối tượng List của bạn.

ví dụ: trong một lớp public static:

public static List<Poll> Fill(this List<Poll> polls) { 
    // code to retrieve polls 
} 

Sau đó, để gọi này, bạn sẽ làm điều gì đó như:

List<Poll> polls = new List<Poll>().Fill(); 
if(polls != null) 
{ 
    // no errors occur 
} 

chỉnh sửa: i chỉ cần thực hiện điều này. bạn có thể hoặc không cần toán tử mới trong List<Poll>().Fill()

0

Vui lòng nêu rõ các giả định, ràng buộc, mong muốn/mục tiêu và lý do của bạn; chúng ta phải đoán và/hoặc đọc suy nghĩ của bạn để biết ý định của bạn là gì.

giả định rằng bạn muốn chức năng của bạn để

  • tạo đối tượng danh sách các cuộc thăm dò
  • đàn áp tất cả các trường hợp ngoại lệ
  • chỉ thành công với một boolean
  • và cung cấp một thông báo lỗi không bắt buộc về thất bại

thì chữ ký ở trên là tốt (mặc dù việc nuốt tất cả các ngoại lệ có thể không phải là một chữ tốt ractice).

Là kiểu mã hóa chung, nó có một số vấn đề tiềm ẩn, như những người khác đã đề cập.

1

Các hướng dẫn nói với cố gắng tránh refra thông số nếu họ không hoàn toàn cần thiết, bởi vì họ làm cho các API khó sử dụng (không chaining hơn về phương pháp này, các nhà phát triển đã tuyên bố tất cả các biến trước Ngoài ra, việc trả lại mã hoặc thông báo lỗi không phải là phương pháp hay nhất, cách tốt nhất là sử dụng ngoại lệ và xử lý ngoại lệ để báo cáo lỗi, các lỗi khác trở nên dễ bỏ qua và có nhiều lỗi hơn khi vượt qua lỗi thông tin xung quanh, trong khi đồng thời mất thông tin có giá trị như stacktrace hoặc ngoại lệ bên trong.

Cách tốt hơn để khai báo phương pháp là như thế này.

public static List<Poll> GetPolls() ... 

và cho lỗi sử dụng báo cáo xử lý

try 
{ 
    var pols = GetPols(); 
    ... 
} catch (DbException ex) { 
    ... // handle exception providing info to the user or logging it. 
} 
0

Ngoài ra còn có mô hình này, như đã thấy trong nhiều chức năng Win32 ngoại lệ.

public static bool GetPolls(out List<Poll> polls) 


if(!PollStuff.GetPolls(out myPolls)) 
    string errorMessage = PollStuff.GetLastError(); 

Nhưng IMO thật khủng khiếp. Tôi sẽ đi cho một cái gì đó ngoại lệ dựa trừ khi phương pháp này đã chạy 65 lần mỗi giây trong một công cụ vật lý trò chơi 3d hoặc someting.

0

Tôi có bỏ lỡ điều gì đó ở đây không? Người hỏi câu hỏi dường như muốn biết cách dọn sạch tài nguyên nếu phương thức thất bại.

public static IList<Poll> GetPolls() 
{ 
    try 
    { 
    } 
    finally 
    { 
     // check that the connection happened before exception was thrown 
     // dispose if necessary 
     // the exception will still be presented to the caller 
     // and the program has been set back into a stable state 
    } 
} 

Lưu ý về phía thiết kế, tôi sẽ xem xét phương thức này để hiểu phương pháp. Toàn bộ ứng dụng, có lẽ, không chịu trách nhiệm lưu trữ và nhận được cuộc thăm dò ý kiến: đó phải là trách nhiệm của một kho dữ liệu.

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