10

私は、イベントを使用してエラーを通知するビジネスおよびデータ層のパターンを使用するコードに取り組んでいます。

resource = AllocateLotsOfMemory();
if (SomeCondition())
{    
    OnOddError(new OddErrorEventArgs(resource.StatusProperty));
    resource.FreeLotsOfMemory();    
    return;
}

これは、特にこれを呼び出すコードがイベントにフックする必要があるため、表面的にはかなり奇妙に見えました (4 つまたは 5 つの異なるイベントがあります!)。

開発者は、この方法でエラー処理コードで割り当てられたリソースのプロパティを参照できること、およびエラー後のクリーンアップの責任はこの層によって保持されることを教えてくれました。

これはある種の意味があります。

代替案は次のようなものかもしれません

resource = AllocateLotsOfMemory();
if (SomeCondition())
{   
    BigObject temporary = resource.StatusProperty;
    resource.FreeLotsOfMemory();
    throw new OddException(temporary);
}

私の質問は次のとおりです。

  1. この " BigObject" は例外オブジェクトが解放されると解放されるので、このパターンは必要ですか?

  2. 他の誰かがこのパターンの経験がありますか? もしそうなら、どのような落とし穴を見つけましたか? どのような利点がありますか?

ありがとう!

4

9 に答える 9

8

私にも奇妙に思えます。複数の「ハンドラー」を許可するなど、いくつかの利点がありますが、セマンティクスは通常のエラー処理とは大きく異なります。特に、スタックに自動的に反映されないという事実が気になります-エラーハンドラー自体が例外をスローしない限り、現在の操作を中止する必要がある場合でも、すべてが正常であるかのようにロジックが続行されます。 .

これについての別の考え方: メソッドが値を返すことを意図しているが、早い段階でエラーを検出したとします。どのような値を返しますか? 例外は、返す適切な値ないという事実を伝えます...

于 2008-09-29T14:29:00.990 に答える
4

これは私には非常に奇妙に見えます。まず、IDisposable はあなたの友達です。それを使用してください。

エラーや例外的な状況に対処する場合は、イベントではなく例外を使用する必要があります。これは、把握、デバッグ、およびコーディングがはるかに簡単であるためです。

だからそうあるべきだ

using(var resource = AllocateLotsOfMemory())
{
   if(something_bad_happened) 
   {
     throw new SomeThingBadException();
   }
}
于 2008-09-29T14:31:27.787 に答える
4

「エラー」と「警告」の観点から考えると、「警告」カテゴリのイベントと「エラー」カテゴリの例外を予約するとき、私は多くの幸運に恵まれました。

ここでの根拠は、イベントはオプションであるということです。誰もあなたの頭に銃を持っていて、あなたにそれらを扱うように強制することはありません. 警告についてはおそらく問題ありませんが、本物のエラーが発生した場合は、もう少し真剣に受け止められるようにする必要があります。例外は処理する必要があります。そうしないと、例外が発生し、ユーザーにとって厄介なメッセージが作成されます。

Big Objectの質問に関しては、大きなオブジェクトを渡しているわけではありませんが、大きなオブジェクトへの参照を渡すことができないという意味ではありません。それができる能力には大きな力があります。

補足として、例外に加えてイベントの発生を止めるものは何もありませんが、繰り返しになりますが、本物のエラーがある場合は、クライアント開発者にそれを処理させる何かが必要です。

于 2008-09-29T14:25:15.600 に答える
3

Udi Dahan によるこの投稿をご覧ください。ドメインイベントをディスパッチするためのエレガントなアプローチです。致命的なエラーから回復するためにイベント メカニズムを使用してはならないという以前の投稿者は正しいですが、疎結合システムでの通知には非常に便利なパターンです。

public class DomainEventStorage<ActionType>
{
    public List<ActionType> Actions
    {
        get
        {
            var k = string.Format("Domain.Event.DomainEvent.{0}.{1}",
                                  GetType().Name,
                                  GetType().GetGenericArguments()[0]);
            if (Local.Data[k] == null)
                Local.Data[k] = new List<ActionType>();

            return (List<ActionType>) Local.Data[k];
        }
    }

    public IDisposable Register(ActionType callback)
    {
        Actions.Add(callback);
        return new DomainEventRegistrationRemover(() => Actions.Remove(callback)
            );
    }
}

public class DomainEvent<T1> : IDomainEvent where T1 : class
{
    private readonly DomainEventStorage<Action<T1>> _impl = new DomainEventStorage<Action<T1>>();

    internal List<Action<T1>> Actions { get { return _impl.Actions; } }

    public IDisposable Register(Action<T1> callback)
    {
        return _impl.Register(callback);
    }

    public void Raise(T1 args)
    {
        foreach (var action in Actions)
        {
            action.Invoke(args);
        }
    }
}

そして消費する:

var fail = false;
using(var ev = DomainErrors.SomethingHappened.Register(c => fail = true) 
{
   //Do something with your domain here
}
于 2008-09-29T14:49:14.577 に答える
3

正直なところ、エラーを通知するイベントは怖いと思います。

ステータス コードを返すことと例外をスローすることに関して、陣営の間で意見の相違があります。単純化するには (非常に) : ステータス コード キャンプでは、例外をスローすると、エラーの原因となっているコードから離れすぎてエラーを検出して処理することになります。例外スローの上限は、ユーザーがステータス コードをチェックするのを忘れ、例外がエラー処理を強制することを示しています。

イベントとしてのエラーは、両方のアプローチの中で最悪のようです。エラーのクリーンアップは、エラーの原因となっているコードから完全に分離されており、エラーの通知は完全に任意です。ああ。

私にとって、メソッドが暗黙的または明示的な契約を満たさなかった場合 (本来の処理を実行しなかった場合)、例外は適切な応答です。この場合、例外で必要な情報をスローすることは合理的です。

于 2008-09-29T14:32:06.317 に答える
3

1) 必要ですか?絶対に必要なパターンはありません

2) Windows Workflow Foundation は、ホストされたランタイム内で実行されているワークフロー インスタンスからのすべての結果を使用してこれを行います。そのイベントを発生させようとすると例外が発生する可能性があることを覚えておいてください。状況に応じて、Dispose または finally ブロックでクリーンアップ コードを実行して、確実に実行されるようにすることをお勧めします。

于 2008-09-29T14:28:00.213 に答える
1

最初のスニペットはおそらく

resource = AllocateLotsOfMemory();
if (SomeCondition())
{
    try
    {
        OnOddError(new OddErrorEventArgs(resource.StatusProperty));
        return;
    }
    finally
    {
        resource.FreeLotsOfMemory();
    }
}

そうしないと、イベントハンドラが例外をスローしたときにリソースを解放できません。

Mike Brownが言ったように、2番目のスニペットも、に設定する代わりにresource.FreeLotsOfMemory()の内容をいじると問題が発生します。resource.StatusPropertynull

于 2009-11-17T08:07:02.707 に答える
0

このアプローチのもう 1 つの大きな問題は、同時実行の問題です。

従来のエラー処理では、制御が制御された方法でコール スタックをエラー ハンドラに移動すると、ロックが解放されます。このスキームでは、イベントが呼び出されたときにすべてのロックが引き続き保持されます。エラー ハンドラー内でブロックが発生すると (ログが記録されている場合はブロックが発生する可能性があります)、デッドロックの原因となる可能性があります。

于 2008-09-29T15:15:35.933 に答える
0

フレームワークのコマンド パターンで使用する基本的な Error オブジェクトと ErrorEvent を使用して、重要でないエラー (検証エラーなど) を処理します。例外と同様に、基本的な ErrorEvent またはより具体的な ErrorEvent をリッスンできます。

また、2 つのスニペットには大きな違いがあります。

resource.FreeLotsOfMemory() が StatusProperty 値を単に null に設定するのではなくクリアすると、OddException が作成されてスローされるときに一時変数に無効なオブジェクトが保持されます。

経験則では、回復不可能な状況でのみ例外をスローする必要があります。C# が、私が Java に本当に欠けている唯一のものである Throws 句をサポートしてくれることを心から願っています。

于 2008-09-29T14:32:01.450 に答える