Giải pháp cho ủy quyền lại không đồng bộ C # 5


16

Vì vậy, một cái gì đó đã làm tôi khó chịu về sự hỗ trợ async mới trong C # 5:

Người dùng nhấn nút bắt đầu hoạt động không đồng bộ. Cuộc gọi trở lại ngay lập tức và bơm thông báo bắt đầu chạy lại - đó là toàn bộ vấn đề.

Vì vậy, người dùng có thể nhấn nút một lần nữa - gây ra lối vào lại. Nếu đây là một vấn đề thì sao?

Trong các bản demo tôi đã thấy, họ vô hiệu hóa nút trước awaitcuộc gọi và bật lại sau đó. Đây dường như là một giải pháp rất mong manh trong một ứng dụng thế giới thực.

Chúng ta có nên mã một số loại máy trạng thái chỉ định điều khiển nào phải bị vô hiệu hóa cho một tập hợp các hoạt động đang chạy không? đây có phải là cách tốt hơn không?

Tôi muốn chỉ hiển thị một hộp thoại theo chế độ trong suốt thời gian hoạt động, nhưng điều này có cảm giác hơi giống với việc sử dụng búa tạ.

Bất cứ ai có bất kỳ ý tưởng tươi sáng, xin vui lòng?


BIÊN TẬP:

Tôi nghĩ rằng việc vô hiệu hóa các điều khiển không nên được sử dụng trong khi một hoạt động đang chạy là rất mong manh bởi vì tôi nghĩ nó sẽ nhanh chóng trở nên phức tạp khi bạn có một cửa sổ có nhiều điều khiển trên đó. Tôi thích giữ mọi thứ đơn giản vì nó làm giảm khả năng xảy ra lỗi, cả trong quá trình mã hóa ban đầu và bảo trì sau đó.

Điều gì xảy ra nếu có một tập hợp các điều khiển nên bị vô hiệu hóa cho một hoạt động cụ thể? Và nếu nhiều hoạt động đang chạy đồng thời thì sao?


Điều gì là mong manh về nó? Nó cung cấp cho người dùng một dấu hiệu gì đó đang được xử lý. Thêm một số công việc chỉ báo động đang được xử lý và đó có vẻ như là giao diện người dùng hoàn toàn tốt đối với tôi.
Steven Jeuris

PS: C # của .NET 4.5 có tên là C # 5 không?
Steven Jeuris

1
Chỉ tò mò tại sao câu hỏi này ở đây và không SO? Đây chắc chắn là về mã vì vậy nó sẽ thuộc về nơi đó tôi nghĩ ...
C. Ross

@ C.Ross, Đây là một câu hỏi về thiết kế / UI. Không thực sự có một điểm kỹ thuật cần được giải thích ở đây.
Morgan Herlocker

Chúng tôi có một vấn đề tương tự ở dạng ajax HTML khi người dùng nhấn nút gửi, yêu cầu ajax được gửi và sau đó chúng tôi muốn chặn người dùng nhấn lại lần nữa, vô hiệu hóa nút là một giải pháp rất phổ biến trong tình huống đó
Raynos

Câu trả lời:


14

Vì vậy, có điều gì đó đã làm tôi khó chịu về hỗ trợ async mới trong C # 5: Người dùng nhấn nút bắt đầu hoạt động không đồng bộ. Cuộc gọi trở lại ngay lập tức và bơm thông báo bắt đầu chạy lại - đó là toàn bộ vấn đề. Vì vậy, người dùng có thể nhấn nút một lần nữa - gây ra lối vào lại. Nếu đây là một vấn đề thì sao?

Hãy bắt đầu bằng cách lưu ý rằng đây đã là một vấn đề, ngay cả khi không có hỗ trợ async trong ngôn ngữ. Rất nhiều thứ có thể khiến tin nhắn bị mất hiệu lực trong khi bạn đang xử lý một sự kiện. Bạn đã phải mã hóa phòng thủ cho tình huống này; tính năng ngôn ngữ mới làm cho điều đó rõ ràng hơn , đó là lòng tốt.

Nguồn phổ biến nhất của vòng lặp thông báo đang chạy trong một sự kiện gây ra sự tái nhập không mong muốn là DoEvents. Điều tuyệt vời awaittrái ngược với DoEventsđiều đó awaitkhiến cho nhiều khả năng các nhiệm vụ mới sẽ không "bỏ đói" các nhiệm vụ hiện đang chạy. DoEventsbơm vòng lặp thông báo và sau đó gọi đồng bộ trình xử lý sự kiện được đăng ký lại, trong khi awaitthông thường sẽ thực hiện nhiệm vụ mới để nó sẽ chạy vào một thời điểm nào đó trong tương lai.

Trong các bản demo tôi đã thấy, họ vô hiệu hóa nút trước cuộc gọi chờ và bật lại sau đó. Tôi nghĩ rằng việc vô hiệu hóa các điều khiển không nên được sử dụng trong khi một hoạt động đang chạy là rất mong manh bởi vì tôi nghĩ nó sẽ nhanh chóng trở nên phức tạp khi bạn có một cửa sổ có nhiều điều khiển trên đó. Điều gì xảy ra nếu có một tập hợp các điều khiển nên bị vô hiệu hóa cho một hoạt động cụ thể? Và nếu nhiều hoạt động đang chạy đồng thời thì sao?

Bạn có thể quản lý mức độ phức tạp đó giống như cách bạn quản lý bất kỳ sự phức tạp nào khác bằng ngôn ngữ OO: bạn trừu tượng hóa các cơ chế thành một lớp và khiến lớp chịu trách nhiệm thực hiện chính sách . (Cố gắng giữ cho các cơ chế khác biệt về mặt logic với chính sách; có thể thay đổi chính sách mà không cần sửa đổi các cơ chế.)

Nếu bạn có một hình thức với nhiều điều khiển trên đó tương tác theo những cách thú vị thì bạn đang sống trong một thế giới phức tạp của chính bạn . Bạn đã phải viết mã để quản lý sự phức tạp đó; nếu bạn không thích điều đó thì hãy đơn giản hóa biểu mẫu để nó không quá phức tạp.

Tôi muốn chỉ hiển thị một hộp thoại theo chế độ trong suốt thời gian hoạt động, nhưng điều này có cảm giác hơi giống với việc sử dụng búa tạ.

Người dùng sẽ ghét điều đó.


Cảm ơn Eric, tôi đoán đây là câu trả lời dứt khoát - tùy thuộc vào nhà phát triển ứng dụng.
Nicholas Butler

6

Tại sao bạn nghĩ vô hiệu hóa nút trước awaitvà sau đó kích hoạt lại khi cuộc gọi hoàn thành là mong manh? Có phải vì bạn lo lắng rằng nút sẽ không bao giờ được kích hoạt lại?

Chà, nếu cuộc gọi không trở lại thì bạn không thể thực hiện lại cuộc gọi, vì vậy có vẻ như đây là hành vi bạn muốn. Điều này cho biết trạng thái lỗi mà bạn nên sửa. Nếu cuộc gọi không đồng bộ của bạn hết thời gian thì điều này vẫn có thể khiến ứng dụng của bạn ở trạng thái không xác định - một lần nữa trong đó việc bật nút có thể gây nguy hiểm.

Lần duy nhất điều này có thể trở nên lộn xộn là nếu có một số thao tác ảnh hưởng đến trạng thái của một nút, nhưng tôi nghĩ những tình huống đó sẽ rất hiếm.


Tất nhiên bạn phải xử lý lỗi - Tôi đã cập nhật câu hỏi của mình
Nicholas Butler

5

Tôi không chắc chắn nếu điều này áp dụng cho bạn vì tôi thường sử dụng WPF, nhưng tôi thấy bạn tham khảo ViewModelvì vậy nó có thể.

Thay vì vô hiệu hóa nút, hãy đặt IsLoadingcờ thành true. Sau đó, bất kỳ yếu tố UI nào bạn muốn tắt có thể được gắn vào cờ. Kết quả cuối cùng là nó trở thành công việc của UI để sắp xếp trạng thái kích hoạt của chính nó, chứ không phải Logic kinh doanh của bạn.

Ngoài ra, hầu hết các nút trong WPF được liên kết với một ICommandvà thông thường tôi đặt ICommand.CanExecutebằng !IsLoading, vì vậy nó sẽ tự động ngăn Lệnh thực thi khi IsLoading bằng với true (cũng vô hiệu hóa nút cho tôi)


3

Trong các bản demo tôi đã thấy, họ vô hiệu hóa nút trước cuộc gọi chờ và bật lại sau đó. Đây dường như là một giải pháp rất mong manh trong một ứng dụng thế giới thực.

Không có gì mong manh về điều này, và đó là những gì người dùng mong đợi. Nếu người dùng cần đợi trước khi nhấn nút một lần nữa thì hãy để họ chờ.

Tôi có thể thấy các kịch bản điều khiển nhất định có thể khiến việc quyết định vô hiệu hóa / kích hoạt bất kỳ lúc nào khá phức tạp, nhưng liệu có đáng ngạc nhiên khi việc quản lý một UI phức tạp sẽ phức tạp không? Nếu bạn có quá nhiều tác dụng phụ đáng sợ từ một điều khiển cụ thể, bạn luôn có thể vô hiệu hóa toàn bộ biểu mẫu và ném một "khối lượng" khổng lồ lên toàn bộ. Nếu đây là trường hợp trên mỗi điều khiển duy nhất, thì giao diện người dùng của bạn có thể không được thiết kế tốt ngay từ đầu (khớp nối chặt chẽ, nhóm điều khiển kém, v.v.).


Cảm ơn - nhưng làm thế nào để quản lý sự phức tạp?
Nicholas Butler

Một lựa chọn sẽ là đưa các điều khiển liên quan vào container. Vô hiệu hóa / kích hoạt bởi container điều khiển, không phải bởi điều khiển. tức là: EditorControls.Foreach (c => c.Enables = false);
Morgan Herlocker

2

Vô hiệu hóa widget là tùy chọn ít phức tạp nhất và dễ bị lỗi. Bạn vô hiệu hóa nó trong trình xử lý trước khi bắt đầu hoạt động async và bạn kích hoạt lại nó vào cuối hoạt động. Vì vậy, vô hiệu hóa + kích hoạt cho mỗi điều khiển được giữ cục bộ để xử lý điều khiển đó.

Bạn thậm chí có thể tạo một trình bao bọc, sẽ lấy một đại biểu và điều khiển (hoặc nhiều hơn trong số chúng), vô hiệu hóa các điều khiển và chạy không đồng bộ một cái gì đó, sẽ thực hiện ủy nhiệm và hơn là kích hoạt lại các điều khiển. Nó sẽ xử lý các trường hợp ngoại lệ (thực hiện kích hoạt cuối cùng), vì vậy nó vẫn hoạt động đáng tin cậy nếu hoạt động thất bại. Và bạn có thể kết hợp nó với việc thêm thông báo vào thanh trạng thái, để người dùng biết những gì anh ta đang chờ đợi.

Bạn có thể muốn thêm một số logic để đếm số lần vô hiệu hóa, vì vậy hệ thống sẽ tiếp tục hoạt động nếu bạn có hai thao tác, cả hai sẽ vô hiệu hóa thao tác thứ ba, nhưng không loại trừ lẫn nhau. Bạn vô hiệu hóa điều khiển hai lần, vì vậy nó sẽ được bật lại chỉ khi bạn bật lại hai lần. Điều đó sẽ bao gồm hầu hết các trường hợp trong khi giữ các trường hợp riêng biệt như bạn có thể nhận được.

Mặt khác, bất cứ thứ gì như máy trạng thái sẽ trở nên phức tạp. Theo kinh nghiệm của tôi, tất cả các máy trạng thái tôi từng thấy rất khó bảo trì và máy trạng thái của bạn sẽ cần phải bao gồm toàn bộ hộp thoại, buộc mọi thứ vào mọi thứ.


Cảm ơn - Tôi thích ý tưởng này. Vì vậy, bạn sẽ có một đối tượng người quản lý cho cửa sổ của mình để đếm số lượng yêu cầu vô hiệu hóa & kích hoạt cho mỗi điều khiển?
Nicholas Butler

@NickButler: Chà, bạn có một đối tượng người quản lý cho mỗi cửa sổ đã có dạng biểu mẫu. Vì vậy, tôi có một lớp thực hiện các yêu cầu và đếm và chỉ cần thêm các thể hiện vào các biểu mẫu có liên quan.
Jan Hudec

1

Mặc dù Jan Hudec và Rachel đã bày tỏ các ý tưởng liên quan, tôi sẽ đề nghị sử dụng một cái gì đó giống như kiến trúc Model-View - thể hiện trạng thái của biểu mẫu trong một đối tượng và gắn các thành phần của biểu mẫu vào trạng thái đó. Điều này sẽ vô hiệu hóa nút theo cách có thể mở rộng cho các kịch bản phức tạp hơn.

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.