C#5非同期再入可能性のソリューション


16

そのため、C#5の新しい非同期サポートについて何かが私を悩ませています。

ユーザーは、非同期操作を開始するボタンを押します。呼び出しはすぐに戻り、メッセージポンプが再び実行を開始します-それがポイントです。

そのため、ユーザーはボタンを再度押すことができます-再入を引き起こします。これが問題になったらどうしますか?

私が見たデモでは、await呼び出しの前にボタンを無効にし、その後再び有効にします。これは、実際のアプリでは非常に脆弱なソリューションのように思えます。

特定の実行中の操作に対してどのコントロールを無効にする必要があるかを指定するある種のステートマシンをコーディングする必要がありますか?または、より良い方法がありますか?

操作中はモーダルダイアログを表示したいだけですが、これはまるでハンマーを使用しているように感じます。

誰もが明るいアイデアを持っていますか?


編集:

操作の実行中に使用すべきでないコントロールを無効にすると、多くのコントロールを持つウィンドウがあるとすぐに複雑になると思うので、壊れやすいと思います。最初のコーディングとその後のメンテナンスの両方で、バグの可能性を減らすため、物事をシンプルに保つことが好きです。

特定の操作に対して無効にする必要があるコントロールのコレクションがある場合はどうなりますか?また、複数の操作が同時に実行されている場合はどうなりますか?


それについて壊れやすいものは何ですか?ユーザーに何かが処理されていることを示します。処理中の動的な指示作業をいくつか追加しますが、これは完全に良いUIのようです。
スティーブンジュリス

PS:.NET 4.5のC#はC#5と呼ばれますか?
スティーブンジュリス

1
なぜこの質問がここにあり、SOではないのですか?それは私が考えてそこに属しますので、これは...コードについて確かである
C.ロス

@ C.Ross、これはデザイン/ UIの質問です。ここで説明する必要のある技術的なポイントはありません。
モーガンハーロッカー

私たちは、ユーザーが送信ボタンをヒットアヤックスHTMLフォーム、AJAXリクエストが送信され、我々は再び提出打つからユーザーをブロックする、ボタンを無効にすると、そのような状況では非常に一般的なソリューションである中、同様の問題持っている
レイノス

回答:


14

そのため、C#5の新しい非同期サポートについて私を悩ませてきました。ユーザーがボタンを押して非同期操作を開始します。呼び出しはすぐに戻り、メッセージポンプが再び実行を開始します-それが全体のポイントです。そのため、ユーザーはボタンを再度押すことができます-再入を引き起こします。これが問題になったらどうしますか?

言語で非同期サポートがなくても、これはすでに問題であることに注意して始めましょう。多くのことが原因で、イベントの処理中にメッセージがデキューされる可能性があります。あなたは既にこのような状況のために防御的にコーディングする必要が。新しい言語機能は、それより明白にするだけで、それは良いことです。

不要な再入を引き起こすイベント中に実行されるメッセージループの最も一般的なソースはDoEventsです。それawaitDoEventsは対照的に、良い点は、await新しいタスクが現在実行中のタスクを「star死」させないようにすることです。通常DoEvents、メッセージループをポンピングし、再入可能なイベントハンドラーを同期的に呼び出しますが、await通常、新しいタスクは将来のある時点で実行されるように新しいタスクをキューに入れます。

私が見たデモでは、待機コールの前にボタンを無効にし、その後再び有効にします。操作の実行中に使用すべきでないコントロールを無効にすると、多くのコントロールを持つウィンドウがあるとすぐに複雑になると思うので、壊れやすいと思います。特定の操作に対して無効にする必要があるコントロールのコレクションがある場合はどうなりますか?また、複数の操作が同時に実行されている場合はどうなりますか?

その複雑さは、オブジェクト指向言語の他の複雑さを管理するのと同じ方法で管理できます。つまり、メカニズムをクラスに抽象化し、クラスにポリシーを正しく実装する責任を負わせます。(メカニズムをポリシーと論理的に区別するようにしてください。メカニズムをいじり回すことなくポリシーを変更することが可能であるべきです。)

あなたが面白い方法で相互作用するたくさんのコントロールがあるフォームを持っているなら、あなたはあなた自身の作成の複雑な世界に住んでいます。その複雑さを管理するコードを作成する必要があります。気に入らない場合は、フォームを単純化して複雑にならないようにします。

操作中はモーダルダイアログを表示したいだけですが、これはまるでハンマーを使用しているように感じます。

ユーザーはそれを嫌います。


エリックのおかげで、これが決定的な答えだと思います-アプリケーション開発者次第です。
ニコラスバトラー

6

await呼び出しの完了前にボタンを無効にしてから再び有効にするのはなぜだと思いますか?ボタンが再び有効にならないことを心配しているからですか?

さて、呼び出しが返されない場合、再度呼び出しを行うことはできませんので、これはあなたが望む動作であるようです。これは、修正する必要があるエラー状態を示します。非同期呼び出しがタイムアウトになった場合、アプリケーションが不確定な状態のままになる可能性があります。この場合も、ボタンを有効にすると危険になる可能性があります。

これが面倒になる可能性があるのは、ボタンの状態に影響を与える操作がいくつかある場合だけですが、そのような状況は非常にまれであると思います。


もちろん、エラーを処理する必要があります-私は私の質問を更新しました
ニコラスバトラー

5

私は通常WPFを使用しているので、これがあなたに当てはまるかどうかはわかりませんが、参照するViewModelのでそうするかもしれません。

ボタンを無効にする代わりに、IsLoadingフラグをに設定しtrueます。次に、無効にするUI要素をフラグにバインドできます。最終的な結果は、ビジネスロジックではなく、有効な状態を整理することがUIの仕事になります。

それに加えて、WPFのほとんどのボタンはにバインドされてICommandおり、通常はにICommand.CanExecute等しく設定されている!IsLoadingため、IsLoadingがtrueの場合にコマンドが実行されないようにします(ボタンも無効になります)


3

私が見たデモでは、待機コールの前にボタンを無効にし、その後再び有効にします。これは、実際のアプリでは非常に脆弱なソリューションのように思えます。

これについて脆​​弱なものは何もなく、ユーザーが期待するものです。ユーザーがボタンをもう一度押す前に待機する必要がある場合は、待機させます。

特定のコントロールシナリオによって、どのコントロールを一度に無効化/有効化するかを決定することが非常に複雑になることがわかりますが、複雑なUIの管理が複雑になるのは驚きですか?特定のコントロールから怖い副作用が多すぎる場合は、常にフォーム全体を無効にし、「ロード」を一気に投げかけます。これがすべての単一のコントロールに当てはまる場合、最初の場所ではUIがあまりうまく設計されていない可能性があります(密結合、不十分なコントロールのグループ化など)。


ありがとう-しかし、どのように複雑さを管理するのですか?
ニコラスバトラー

1つのオプションは、関連するコントロールをコンテナーに配置することです。コントロールではなく、コントロールコンテナによって無効/有効にします。例:EditorControls.Foreach(c => c.Enabled = false);
モーガンハーロッカー

2

ウィジェットの無効化は、最も複雑でエラーが発生しやすいオプションです。非同期操作を開始する前にハンドラーで無効にし、操作の最後に再度有効にします。したがって、各コントロールのdisable + enableは、そのコントロールの処理に対してローカルに保持されます。

ラッパーを作成して、デリゲートとコントロール(またはそれ以上)を受け取り、コントロールを無効にして非同期的に実行し、デリゲートを実行してからコントロールを再度有効にすることもできます。例外を処理する必要があり(最終的に有効にする)、操作が失敗した場合でも確実に動作します。また、ステータスバーにメッセージを追加することと組み合わせることができるため、ユーザーは何を待っているのかがわかります。

無効化をカウントするためのロジックを追加すると、2つの操作がある場合にシステムが動作し続けることがあります。両方の操作で3つ目の操作を無効にする必要があります。コントロールを2回無効にすると、再び2回有効にした場合にのみ再び有効になります。ほとんどのケースをカバーしながら、できるだけ多くのケースを分離する必要があります。

一方、ステートマシンのようなものはすべて複雑になります。私の経験では、これまで見たすべてのステートマシンは維持が難しく、ステートマシンはダイアログ全体を包含し、すべてをすべてに結び付ける必要があります。


ありがとう-私はこのアイデアが好きです。それでは、各コントロールの無効化および有効化リクエストの数をカウントするウィンドウのマネージャーオブジェクトがありますか?
ニコラスバトラー

@NickButler:さて、あなたはすでに各ウィンドウのマネージャーオブジェクト、つまりフォームを持っています。そのため、リクエストを実装してカウントするクラスを用意し、関連するフォームにインスタンスを追加するだけです。
ジャン・ヒューデック

1

Jan HudecとRachelは関連するアイデアを表明していますが、Model-Viewアーキテクチャのようなものを使用することをお勧めします。オブジェクトのフォームの状態を表現し、フォーム要素をその状態に結び付けます。これにより、より複雑なシナリオに対応できるようにボタンが無効になります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.