3

クライアント/サーバーアプリケーションの「client」クラスに非同期コマンドパターンを実装しています。私は過去にいくつかのソケットコーディングを行ったことがあり、Socket/SocketAsyncEventArgsクラスで使用されている新しい非同期パターンが気に入っています。

私の非同期メソッドは次のようpublic bool ExecuteAsync(Command cmd);になります。実行が保留中の場合はtrueを返し、同期的に完了した場合はfalseを返します。私の質問は、例外が発生した場合でも、常にコールバック(cmd.OnCompleted)を呼び出す必要がありますか?または、ExecuteAsyncから直接例外をスローする必要がありますか?

必要に応じて、詳細を以下に示します。これはSocketAsyncEventArgsの使用に似ていますが、SocketAsyncEventArgsの代わりに私のクラスはSomeCmdと呼ばれます。

SomeCmd cmd = new SomeCmd(23, 14, 10, "hike!");
cmd.OnCompleted += this.SomeCmd_OnCompleted;
this.ConnectionToServer.ExecuteAsync(cmd);

Socketクラスと同様に、コールバックメソッド(この場合はSomeCmd_OnCompleted)と調整する必要がある場合は、ExecuteAsyncの戻り値を使用して、操作が保留中(true)かどうか、または操作が同期的に完了したかどうかを知ることができます。

SomeCmd cmd = new SomeCmd(23, 14, 10, "hike!");
cmd.OnCompleted += this.SomeCmd_OnCompleted;
if( this.ConnectionToServer.ExecuteAsync(cmd) )
{
    Monitor.Wait( this.WillBePulsedBy_SomeCmd_OnCompleted );
}

これが私の基本クラスの非常に単純化されたバージョンですが、それがどのように機能するかを見ることができます:

class Connection
{
    public bool ExecuteAsync(Command cmd)
    {
        /// CONSIDER: If you don't catch every exception here
        /// then every caller of this method must have 2 sets of
                /// exception handling:
        /// One in the handler of Command.OnCompleted and one where ExecuteAsync
        /// is called.
        try
        {
        /// Some possible exceptions here:
        /// 1) remote is disposed. happens when the other side disconnects (WCF).
        /// 2) I do something wrong in TrackCommand (a bug that I want to fix!)
            this.TrackCommand(cmd);
            remote.ServerExecuteAsync( cmd.GetRequest() );
            return true;
        }
        catch(Exception ex)
        {
            /// Command completing synchronously.
            cmd.Completed(ex, true);
            return false;
        }
    }
    /// <summary>This is what gets called by some magic when the server returns a response.</summary>
    internal CommandExecuteReturn(CommandResponse response)
    {
        Command cmd = this.GetTrackedCommand(response.RequestId);
        /// Command completing asynchronously.
        cmd.Completed(response, false);
    }

    private IServer remote;
}

abstract class Command: EventArgs
{
    internal void Completed(Exception ex, bool synchronously)
    {
        this.Exception = ex;

        this.CompletedSynchronously = synchronously;

        if( this.OnCompleted != null )
        {
            this.OnCompleted(this);
        }
    }

    internal void Completed(CommandResponse response, bool synchronously)
    {
        this.Response = response;
        this.Completed(response.ExceptionFromServer, synchronously)
    }

    public bool CompletedSynchronously{ get; private set; }

    public event EventHandler<Command> OnCompleted;

    public Exception Exception{ get; private set; }

    internal protected abstract CommandRequest GetRequest();
}
4

5 に答える 5

5

.NET での非同期操作の一般的なパターンの 1 つ (少なくともBackgroundWorkerBeginInvoke()/EndInvoke()メソッドのペアの場合は、実際の戻り値または発生した例外からコールバックを分離する結果オブジェクトを用意することです。例外を処理するのはコールバックの責任です。

C# ライクな擬似コード:


private delegate int CommandDelegate(string number);

private void ExecuteCommandAsync()
{
    CommandDelegate del = new CommandDelegate(BeginExecuteCommand);
    del.BeginInvoke("four", new AsyncCallback(EndExecuteCommand), null);
}

private int BeginExecuteCommand(string number)
{
   if (number == "five")
   {
      return 5;
   }
   else
   {
      throw new InvalidOperationException("I only understand the number five!");
   }
}

private void EndExecuteCommand(IAsyncResult result)
{
    CommandDelegate del;
    int retVal;

    del = (CommandDelegate)((AsyncResult)result).AsyncDelegate;

    try
    {
        // Here's where we get the return value
        retVal = del.EndInvoke(result);
    }
    catch (InvalidOperationException e)
    {
        // See, we had EndExecuteCommand called, but the exception
        // from the Begin method got tossed here
    }
}

したがって、 を呼び出すとExecuteCommandAsync()、すぐに返されます。はBeginExecuteCommand()別のスレッドで起動されます。例外を投げる場合、その例外は、 を呼び出すまでスローされませんEndInvoke()(IAsyncResultこれは にキャストできAsyncResultます。これは文書化されていますが、キャストが不快な場合は状態で渡すことができます。このようにして、例外処理コードメソッドの戻り値とやり取りする場所の周りに「自然に配置」されます。

詳細については、MSDN で IAsyncResult パターンの詳細を確認してください。

お役に立てれば。

于 2008-11-18T05:37:42.870 に答える
4

ExecuteAsyncで例外をスローせず、代わりにコールバックの例外条件を設定しますこれにより、非同期ロジックに対して一貫したプログラミング方法が作成され、反復コードが削減されます。クライアントはこのクラスを呼び出して、例外を処理する1つの方法を期待できます。これにより、バグが少なく、脆弱性の少ないコードが提供されます。

于 2008-11-18T02:51:06.247 に答える
3

ディスパッチポイントから例外をスローすると、役立つ場合と役に立たない場合があります

例外引数を渡してコールバックを呼び出すには、2つの異なることを行うために完了コールバックが必要です

代わりに、例外レポートの2番目のコールバックが理にかなっている場合があります

于 2008-11-18T03:01:15.837 に答える
1

カスタム例外をスローし、完了したコールバックを呼び出さないでしょう。結局のところ、例外が発生した場合、コマンドは完了しませんでした。

于 2008-11-18T02:41:14.350 に答える
0

1 つの場所で例外を処理する方がはるかに簡単です。次の区別を使用します。処理する必要がある例外については、コールバックでスローします。クラスの使用が簡単になります。キャッチしてはならない例外 (ArgumentException など) は、ExecuteAsync でスローします。未処理の例外ができるだけ早く爆発することを望んでいます。

于 2008-11-18T04:29:01.557 に答える