Решения для асинхронного повторного входа C # 5


16

Итак, меня что-то беспокоило в связи с новой поддержкой асинхронности в C # 5:

Пользователь нажимает кнопку, которая запускает асинхронную операцию. Вызов немедленно возвращается, и насос сообщений снова начинает работать - вот и весь смысл.

Таким образом, пользователь может снова нажать кнопку - вызывая повторный вход. Что если это проблема?

В демонстрациях, которые я видел, они отключают кнопку перед awaitвызовом и включают ее снова после. Это кажется мне очень хрупким решением в реальном приложении.

Должны ли мы кодировать какой-то конечный автомат, который указывает, какие элементы управления должны быть отключены для данного набора запущенных операций? Или есть лучший способ?

Я испытываю желание просто показать модальное диалоговое окно на время операции, но это похоже на использование кувалды.

У кого-нибудь есть какие-нибудь яркие идеи, пожалуйста?


РЕДАКТИРОВАТЬ:

Я думаю, что отключение элементов управления, которые не должны использоваться во время выполнения операции, является хрупким, потому что я думаю, что это быстро станет сложным, когда у вас будет окно с множеством элементов управления. Мне нравится быть проще, потому что это уменьшает вероятность ошибок, как во время первоначального кодирования, так и при последующем обслуживании.

Что если существует набор элементов управления, которые следует отключить для определенной операции? А что, если несколько операций выполняются одновременно?


Что в этом хрупкого? Это дает пользователю указание, что что-то обрабатывается. Добавьте динамическую индикацию, работа обрабатывается, и мне кажется, что это очень хороший интерфейс.
Стивен Джеурис

PS: C # .NET 4.5 называется C # 5?
Стивен Джеурис

1
Просто любопытно, почему этот вопрос здесь, а не ТАК? Это определенно о коде, так что он будет там, я думаю ...
К. Росс

@ C.Ross, это больше вопрос дизайна / интерфейса. На самом деле нет технической точки зрения, которая должна быть объяснена здесь.
Морган Херлокер

У нас похожая проблема в HTML-формах ajax, когда пользователь нажимает кнопку отправки, отправляется запрос ajax, и затем мы хотим заблокировать повторное нажатие пользователем кнопки отправки, отключение кнопки является очень распространенным решением в этой ситуации
Raynos

Ответы:


14

Итак, что-то меня беспокоит в связи с новой поддержкой асинхронности в C # 5: пользователь нажимает кнопку, которая запускает асинхронную операцию. Вызов немедленно возвращается, и насос сообщений снова начинает работать - вот и весь смысл. Таким образом, пользователь может снова нажать кнопку - вызывая повторный вход. Что если это проблема?

Давайте начнем с того, что отметим, что это уже проблема, даже без асинхронной поддержки языка. Многие вещи могут привести к тому, что сообщения будут удалены из очереди, пока вы обрабатываете событие. Вы уже должны защищаться для этой ситуации; новая языковая особенность только делает это более очевидным , что является добром.

Наиболее распространенным источником цикла сообщений, выполняющегося во время события, вызывающего нежелательный повторный вход, является DoEvents. Хорошая вещь awaitв противоположность тому DoEvents, что awaitэто повышает вероятность того, что новые задачи не будут "истощать" текущие задачи. DoEventsпрокачивает цикл сообщений, а затем синхронно вызывает повторно входящий обработчик событий, в то время как awaitобычно ставит в очередь новую задачу, чтобы она выполнялась в какой-то момент в будущем.

В демонстрациях, которые я видел, они отключают кнопку перед вызовом await и снова включают ее. Я думаю, что отключение элементов управления, которые не должны использоваться во время выполнения операции, является хрупким, потому что я думаю, что это быстро станет сложным, когда у вас будет окно с множеством элементов управления. Что если существует набор элементов управления, которые следует отключить для определенной операции? А что, если несколько операций выполняются одновременно?

Вы можете управлять этой сложностью так же, как и любой другой сложностью в ОО-языке: вы абстрагируете механизмы в класс и возлагаете на класс ответственность за правильную реализацию политики . (Постарайтесь, чтобы механизмы логически отличались от политики; должна быть возможность изменить политику, не повреждая слишком много механизмов.)

Если у вас есть форма с множеством элементов управления, которые взаимодействуют интересным образом, то вы живете в мире сложности своего собственного создания . Вы должны написать код, чтобы справиться с этой сложностью; если вам это не нравится, то упростите форму, чтобы она не была такой сложной.

Я испытываю желание просто показать модальное диалоговое окно на время операции, но это похоже на использование кувалды.

Пользователи будут ненавидеть это.


Спасибо, Эрик, я думаю, что это окончательный ответ - дело за разработчиком приложения.
Николас Батлер

6

Как вы думаете, почему отключение кнопки до, awaitа затем ее повторное включение после завершения вызова является хрупким? Это потому, что вы беспокоитесь о том, что кнопка никогда не будет снова включена?

Что ж, если звонок не возвращается, вы не можете позвонить снова, поэтому кажется, что вы хотите именно этого. Это указывает на состояние ошибки, которое вы должны исправить. Если время вашего асинхронного вызова истекло, это может привести к тому, что ваше приложение останется в неопределенном состоянии - опять же, в котором включение кнопки может быть опасным.

Единственный случай, когда это может запутаться, - это если несколько операций влияют на состояние кнопки, но я думаю, что такие ситуации должны быть очень редкими.


Конечно, вы должны обрабатывать ошибки - я обновил свой вопрос
Николас Батлер

5

Я не уверен, относится ли это к вам, так как я обычно использую WPF, но я вижу, что вы ссылаетесь на это ViewModelтак.

Вместо отключения кнопки установите IsLoadingфлаг в true. Тогда любой элемент пользовательского интерфейса, который вы хотите отключить, может быть привязан к флагу. Конечным результатом является то, что задача пользовательского интерфейса - сортировать собственное включенное состояние, а не бизнес-логику.

В дополнение к этому, большинство кнопок в WPF связаны с ICommand, и обычно я устанавливаю значение ICommand.CanExecuteравно !IsLoading, поэтому оно автоматически предотвращает выполнение команды, когда IsLoading равно true (также отключает кнопку для меня)


3

В демонстрациях, которые я видел, они отключают кнопку перед вызовом await и снова включают ее. Это кажется мне очень хрупким решением в реальном приложении.

В этом нет ничего хрупкого, и это то, что пользователь ожидает. Если пользователю нужно подождать, прежде чем снова нажать кнопку, заставьте его ждать.

Я могу видеть, как определенные сценарии управления могут принимать решение о том, какие элементы управления отключать / включать в одно и то же время довольно сложно, но удивительно ли, что управление сложным пользовательским интерфейсом будет сложным? Если у вас слишком много страшных побочных эффектов от конкретного элемента управления, вы всегда можете просто отключить всю форму и запустить «загрузку» вихревого концерта над всем этим. Если это относится к каждому отдельному элементу управления, то ваш пользовательский интерфейс, вероятно, не очень хорошо спроектирован в первую очередь (жесткая связь, плохая группировка элементов управления и т. Д.).


Спасибо - а как управлять сложностью?
Николас Батлер

Один из вариантов - поместить соответствующие элементы управления в контейнер. Отключить / включить контейнером управления, а не контролем. то есть: EditorControls.Foreach (c => c.Enabled = false);
Морган Херлокер

2

Отключение виджета является наименее сложным и подверженным ошибкам вариантом. Вы отключаете его в обработчике перед началом асинхронной операции и снова включаете его в конце операции. Таким образом, disable + enable для каждого элемента управления остаются локальными для обработки этого элемента управления.

Вы даже можете создать оболочку, которая будет принимать делегат и элемент управления (или больше из них), отключать элементы управления и асинхронно запускать что-то, что будет выполнять делегирование и затем повторно включать элементы управления. Он должен позаботиться об исключениях (сделайте включение в, наконец), чтобы он по-прежнему работал надежно, если операция не удалась. И вы можете объединить это с добавлением сообщения в строке состояния, чтобы пользователь знал, чего он ждет.

Возможно, вы захотите добавить некоторую логику для подсчета отключений, чтобы система продолжала работать, если у вас есть две операции, которые должны отключить третью, но не являются взаимоисключающими. Вы дважды отключаете элемент управления, поэтому он снова станет активным, только если вы включите его дважды. Это должно охватывать большинство случаев, сохраняя при этом отдельные случаи настолько, насколько это возможно.

С другой стороны, что-то вроде конечного автомата станет сложным. По моему опыту, все конечные автоматы, которые я когда-либо видел, были трудны в обслуживании, и ваш конечный автомат должен был охватывать весь диалог, связывая все со всем.


Спасибо - мне нравится эта идея. Итак, у вас есть объект менеджера для вашего окна, который подсчитывает количество запросов на отключение и включение для каждого элемента управления?
Николас Батлер

@NickButler: Ну, у вас уже есть объект менеджера для каждого окна - форма. Так что у меня будет класс, реализующий запросы и подсчитывающий, и просто добавляю экземпляры в соответствующие формы.
Ян Худек

1

Хотя Ян Худек и Рэйчел высказали связанные идеи, я бы предложил использовать что-то вроде архитектуры Model-View - выразить состояние формы в объекте и связать элементы формы с этим состоянием. Это отключило бы кнопку таким образом, чтобы она могла масштабироваться для более сложных сценариев.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.