Cách thanh lịch nhất để viết một phương pháp Thử Thử trong C # 7 là gì?


21

Tôi đang viết một kiểu triển khai Hàng đợi có một TryDequeuephương thức sử dụng một mẫu tương tự như các TryParsephương thức .NET khác nhau , trong đó tôi trả về giá trị boolean nếu hành động thành công và sử dụng một outtham số để trả về giá trị bị mất thực tế.

public bool TryDequeue(out Message message) => _innerQueue.TryDequeue(out message);

Bây giờ, tôi muốn tránh outparams bất cứ khi nào tôi có thể. C # 7 cung cấp cho chúng tôi các phương tiện biến đổi khác nhau để làm việc với chúng dễ dàng hơn, nhưng tôi vẫn xem xét các thông số cần thiết hơn là một công cụ hữu ích.

Hành vi tôi muốn từ phương pháp này như sau:

  • Nếu có một mục để dequeue, trả lại nó.
  • Nếu không có mục nào để dequeue (hàng đợi trống), hãy cung cấp cho người gọi đủ thông tin để hành động phù hợp.
  • Đừng chỉ trả lại một mục rỗng nếu không còn mục nào.
  • Đừng ném một ngoại lệ nếu cố gắng giải quyết từ một hàng đợi trống.

Ngay bây giờ, một người gọi phương thức này hầu như sẽ luôn sử dụng một mẫu như sau (sử dụng cú pháp biến C # 7 out):

if (myMessageQueue.TryDequeue(out Message dequeued))
    MyMessagingClass.SendMessage(dequeued)
else
    Console.WriteLine("No messages!"); // do other stuff

Đó không phải là điều tồi tệ nhất, tất cả đã nói. Nhưng tôi không thể không cảm thấy có nhiều cách tốt hơn để làm điều này (tôi hoàn toàn sẵn sàng thừa nhận rằng có thể không có). Tôi ghét cách người gọi phải phá vỡ dòng chảy của nó với một điều kiện khi tất cả những gì nó muốn là nhận được một giá trị nếu có.

Một số mô hình khác tồn tại để thực hiện hành vi "thử" này là gì?

Đối với ngữ cảnh, phương thức này có thể có khả năng được gọi trong các dự án VB, vì vậy điểm thưởng cho thứ gì đó hoạt động tốt trong cả hai. Thực tế này nên mang rất ít trọng lượng, mặc dù.


9
Xác định một Option<T>cấu trúc và trả lại nó. Những bool Try(..., out data)chức năng đó là một sự gớm ghiếc.
CodeInChaos

2
Tôi đã suy nghĩ tương tự ... Có thể <T>, Tùy chọn <T> nếu một điều bất lợi với các tham số OUT.
Jon Raynor

1
@FrustratedWithFormsDesigner, tôi chưa "thử" nhiều thứ khác (chơi chữ được hoan nghênh), nhiều như đã nghĩ về chúng. Tôi đã có ý tưởng trả lại một ValueTuple nhưng tốt nhất tôi không nghĩ rằng điều đó mang lại nhiều cải tiến.
Eric Sondergard

@CodesInChaos Chúng tôi ở cùng một trang, đó là lý do tôi ở đây! Và tôi thích ý tưởng này. Nếu bạn có thời gian, bạn có quan tâm để cung cấp thêm một số chi tiết trong một câu trả lời để tôi có thể chấp nhận nó không?
Eric Sondergard

Câu trả lời:


24

Sử dụng loại Tùy chọn, nghĩa là một đối tượng thuộc loại có hai phiên bản, thường được gọi là "Một số" (khi có giá trị) hoặc "Không" (khi không có giá trị) ... Hoặc đôi khi họ được gọi là Chỉ và Không có gì. Sau đó, có các hàm trong các loại này cho phép bạn truy cập giá trị nếu có, kiểm tra sự hiện diện và quan trọng nhất là phương pháp cho phép bạn áp dụng một hàm trả về Tùy chọn tiếp theo cho giá trị nếu nó xuất hiện (thường là trong C # được gọi là FlatMap mặc dù trong các ngôn ngữ khác, nó thường được gọi là Bind thay vào đó ... Tên rất quan trọng trong C # vì có phương thức của tên này và gõ cho phép bạn sử dụng các đối tượng Tùy chọn của mình trong các câu lệnh LINQ).

Các tính năng bổ sung có thể bao gồm các phương thức như IfPftime và IfNotPftime để gọi các hành động trong các điều kiện có liên quan và OrElse (thay thế một giá trị mặc định khi không có giá trị nhưng không có giá trị khác), v.v.

Ví dụ của bạn có thể trông giống như:

myMessageQueue.TryDeque()
    .IfPresent( dequeued => MyMessagingClass.SendMessage(dequeued))
    .IfNotPresent (() =>  Console.WriteLine("No messages!")

Đây là mô hình đơn nguyên Tùy chọn (hoặc Có thể), và nó cực kỳ hữu ích. Có các triển khai hiện có (ví dụ: https://github.com/nlkl/Optional/blob/master/README.md ) nhưng điều đó cũng không khó đối với chính bạn.

(Bạn có thể mở rộng mẫu này để trả về mô tả nguyên nhân lỗi thay vì không có gì khi phương thức không thành công ... Điều này hoàn toàn có thể đạt được và thường được gọi là đơn vị Either; vì tên này ngụ ý bạn có thể sử dụng cùng một FlatMap mẫu để làm cho nó dễ dàng để làm việc trong trường hợp đó, quá)


Đây chắc chắn là một lựa chọn tốt, cảm ơn lời đề nghị và suy nghĩ của bạn. Tôi đã thực sự tìm cách khám phá thêm ý tưởng ở đây.
Eric Sondergard

2
Điều tốt đẹp về Option/ Maybelà nó là một đơn nguyên (tất nhiên, nếu được thực hiện như một), điều đó có nghĩa là nó có thể được xâu chuỗi, quấn, tháo và xử lý một cách an toàn mà không cần phải xử lý trực tiếp các trường hợp khác nhau, chuỗi này và việc xử lý có thể được thực hiện với LINQ Query Expressions.
Jörg W Mittag

3
Nhân tiện, flatMap/ bindđược gọi SelectManytrong .NET.
Jörg W Mittag

5
Tôi tự hỏi liệu có nên chọn một tên khác hay không, vì bằng cách này, bạn đang phá vỡ Try...các quy ước đặt tên trong .NET (và các nhà bảo trì có thể gây nhầm lẫn).
Bob

@Bob Đó là một điểm tuyệt vời. Tôi đồng ý.
Eric Sondergard

12

Các câu trả lời là tốt và tôi sẽ đi với một trong số họ. Hãy xem xét câu trả lời này chỉ là điền vào một vài góc với một số ý tưởng thay thế:

  • Tạo các lớp con của Messagegọi SomeMessageNoMessage. DequeueBây giờ có thể quay lại NoMessagenếu không có tin nhắn và SomeMessagenếu có tin nhắn. Nếu callee quan tâm để phát hiện trường hợp nào họ đang ở, họ có thể dễ dàng thực hiện bằng cách kiểm tra loại. Nếu họ không, tốt, chỉ cần làm NoMessagebất cứ điều gì bất cứ khi nào bất kỳ phương thức nào của nó được gọi, và hey, họ nhận được những gì họ yêu cầu.

  • Giống như trên, nhưng làm cho Messagehoàn toàn có thể chuyển đổi thành bool(hoặc thực hiện operator true / operator false). Bây giờ bạn có thể nói if (message)và có đúng nếu thông điệp tốt và sai nếu nó xấu. (Đây gần như chắc chắn là một ý tưởng tồi, nhưng tôi bao gồm nó cho đầy đủ!)

  • C # 7 có bộ dữ liệu. Trả lại một (bool, Message)tuple.

  • Tạo Messagemột kiểu cấu trúc và trả về Message?.


Này, cảm ơn câu trả lời của bạn. Với câu hỏi này, tôi đã cố gắng thể hiện bản thân với những ý tưởng khác nhau cũng như tôi đang cố gắng tìm ra giải pháp cho vấn đề cụ thể của mình. Chúc mừng!
Eric Sondergard

Ý tôi là, nếu "ý tưởng tồi" của bạn được Unity sử dụng, tại sao chúng ta không thể sử dụng nó?
Arturo Torres Sánchez

@ ArturoTorresSánchez: Câu hỏi của bạn là "người khác đã sử dụng một thực tiễn lập trình thực sự tồi tệ, vậy tại sao tôi không thể?" Câu hỏi không mạch lạc. Không ai ngăn bạn sử dụng các thực hành lập trình xấu giống như người khác. Bạn đi thẳng về phía trước. Điều đó sẽ không làm cho nó trở thành một thực hành tốt trong C #.
Eric Lippert

Đó là lưỡi trong má. Tôi đoán tôi cần phải sử dụng "/ s" để đánh dấu nó.
Arturo Torres Sánchez

@ ArturoTorresSánchez: Đây là trang web câu hỏi và trả lời; Tôi coi câu hỏi là câu hỏi đang tìm kiếm câu trả lời .
Eric Lippert

10

Trong C # 7, bạn có thể sử dụng khớp mẫu để đạt được điều tương tự theo cách thanh lịch hơn:

if (myMessageQueue.TryDequeue() is Message dequeued) 
{
     MyMessagingClass.SendMessage(dequeued)
} 
else 
{
    Console.WriteLine("No messages!"); // do other stuff
}

Trong trường hợp cụ thể này, tôi có thể sẽ sử dụng các sự kiện thay vì bỏ phiếu liên tục.


3
Không phải điều đó mặc dù yêu cầu TryDequeuephải trả lại một số giao diện đánh dấu với một Messagetriển khai và một số thực hiện "không có gì"? Chắc chắn, nó thanh lịch hơn tại trang web cuộc gọi, nhưng bạn bị mắc kẹt khi thực hiện 3 triển khai trong mã thực tế, điều này có thể là không thể nếu Messagelà một loại hiện có.
Telastyn

1
@Telastyn: Nó cũng hoạt động nếu nó chỉ trả về null . Bạn cũng có thể sử dụng một loại Tùy chọn.
JacquesB

@JacquesB: nhưng nếu null là một hàng có thể xếp hàng hợp lệ thì sao?
JBSnorro

5

Không có gì sai với phương pháp Thử ... (có tham số out) cho một kịch bản trong đó thất bại (không có giá trị) cũng phổ biến như thành công.

Nhưng, nếu bạn khăng khăng làm khác đi, bạn có thể muốn trả lại một tin nhắn trống và chỉ gửi nó (không phải vấn đề của bạn nữa) hoặc hoãn lại câu lệnh if cho đến khi bạn thực sự phải biết liệu bạn có tin nhắn có nội dung hay không.

Lưu ý rằng đôi khi ai đó phải làm bài kiểm tra. Tôi cho rằng bài kiểm tra nên được thực hiện khi nào và ở đâu câu hỏi hiện tại. Đây là vào thời điểm của dequying.


Bạn làm cho một điểm tốt. Bạn vẫn phải kiểm tra, bất kể bạn làm gì. Thành thật mà nói, tôi vẫn có thể đưa ra các tham số tại thời điểm này (thực tế hiện tại tôi đang phơi bày nhiều triển khai "TryDequeue" chỉ để miệt mài với từng tham số). Tôi thực sự chỉ muốn thảo luận về các lựa chọn khác có thể ở ngoài đó.
Eric Sondergard
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.