1

問題:

私は、時間のかかる操作のために、キャンセルボタン付きのフォーム (WinForm) にプログレスバーを表示することになっているアプリケーションに取り組んでいます。したがって、明らかに私は BackgroundWorker スレッドを使用しています。以下は、私が達成しようとしていることを大まかにシミュレートするコードです。

namespace WindowsFormsApplication1
{
    public delegate void SomeDelegateHandler();

    public partial class Form1 : Form
    {
        public event SomeDelegateHandler DoSomeAction;
        BackgroundWorker bgWorker;

        public Form1()
        {
            InitializeComponent();

            bgWorker = new BackgroundWorker();
            bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
        }

        void bgWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            //Some logic code here.
            for (int i = 0; i < 100; i++)
            {
                DoSomeAction();
            }
        }       

        private void Form1_Shown(object sender, EventArgs e)
        {
            if (DoSomeAction != null)
                bgWorker.RunWorkerAsync();
            else throw new EventNotSubscribedException();//Is this a valid style??
        }
    }

    public class EventNotSubscribedException : ApplicationException
    {
       //Some custom code here
    }
}

私の解決策

上記のコードに従って、フォームがユーザーに表示されるとすぐに (OnShown イベント)、backgroundworker スレッドを開始します。これは、ユーザーがこれを行うためにアクションを開始する必要がないためです。そのため、onshown は時間のかかる操作ジョブを実行します。しかし、問題は、上で示したように、時間のかかる主なジョブが他のクラス/コンポーネントで実行されることです。そこでは、それも厳密に制限されています(レガシーコード:リファクタリングできません)。したがって、このフォームを起動するレガシ コード クラスのイベント DoSomeAction にサブスクライブしました。

疑問/質問:

上記のように例外をスローすることは有効ですか? (以下の私の理由を読んでください)。

理由:

OnShown イベントは、イベント ハンドラー オブジェクトの null をチェックします。これは、このフォームを使用可能にするには、サブスクライバー (使用コード) がイベントをサブスクライブする必要があるためです。そうでない場合、フォームは表示されるだけでまったく通知されず、使用コードはなぜそうなっているのかわからない可能性があります。使用法コードは、ボタン クリック イベントと同様に、イベントへのサブスクライブがオプションであると想定する場合があります。

私の投稿が明確でわかりやすいことを願っています。

ありがとう & 幸せなコーディング、Zen :)

4

2 に答える 2

1

消費するコードで BackgroundWorker を構築し、それをフォームのコンストラクターに渡すことをお勧めします。コンストラクターで null テストを実行し、この問題全体を回避できます。または、代わりにデリゲートをコンストラクター引数として使用します。つまり、消費するコードが操作中にワーカー デリゲートを変更する必要がある可能性はどのくらいあるのでしょうか?


もう 1 つの方法は、ダイアログでタスクを制御するのではなく、ダイアログでタスクを監視することです (ここにあるように)。たとえば、次のようなインターフェイスを持つことができます。

public interface IMonitorableTask {
    void Start();

    event EventHandler<TData> TaskProgress;
}

は、ダイアログTDataを更新するために必要な情報 (完了率など) を提供する型です。

これの欠点は、各タスクが独自のタイプである必要があることです。これにより、非常に見苦しく雑然としたコードが作成される可能性があります。次のようなヘルパー クラスを作成することで、この問題を多少軽減できます。

public class DelegateTask : IMonitorableTask {
    private Action<Action<TData>> taskDelegate;

    public event EventHandler<TData> TaskProgress;

    public DelegateTask(Action<Action<TData>> taskDelegate) {
        if (taskDelegate == null)
            throw new ArgumentNullException("taskDelegate");

        this.taskDelegate = taskDelegate;
    }

    protected void FireTaskProgress(TData data) {
        var handler = TaskProgress;

        if (handler != null)
            handler(this, data);
    }

    public void Start() {
        taskDelegate(FireTaskProgress);
    }
}

次に、タスク メソッドがファクトリになります。

public IMonitorableTask CreateFooTask(object argument) {
    return new DelegateTask(progress => {
        DoStuffWith(argument);

        progress(new TData(0.5));

        DoMoreStuffWith(argument);

        progress(new TData(1));
    });
}

そして今、コマンドラインインターフェースなどを簡単に(*) サポートできるようになりました。タスクのイベントに別の監視オブジェクトを添付するだけです。

(*) もちろん、UI/ロジックの分離がどれだけきれいかによって異なります。

于 2012-05-30T00:07:35.940 に答える
1

フォームの呼び出し元に例外をスローする必要があるということですか? showDialog または Show を使用して呼び出されますか?

ところで、私はイベントから例外を生成したくありません。むしろ Form クラスに何らかのステータスが設定されているところから戻るようにしておくとよいでしょう。

たとえば、私は

IsEventSubscribe = false this.Close()

EventNotSubscribeException ではなく

ところで、コードで確認できる問題の 1 つは、bgWorker_DoWork が呼び出されたときに、DoSomeAction を null にチェックする必要があります。そうしないと、NullReferenceException が発生する可能性があるためです。

好ましい、

  1. Form_shown から RunWorkerAsync の実行を開始します
  2. DoWork でデリゲートを null にチェックし、null の場合は DoSomeAction を呼び出さないでください。それ以外の場合は呼び出します。
  3. BackgroundWorker の RunWorkerCompleted で、フォームを閉じます。

さらに何か必要な場合はお知らせください。

于 2012-05-29T21:14:38.973 に答える