-1

このスニペットを検討してください。

class Foo
{
    public event Action Event;
    public void TriggerEvent()
    {
        if (Event != null) {
            Event();
        }
    }
}

static void Handler()
{
    Console.WriteLine("hi!");
}

static void Main()
{
    var obj = new Foo();
    obj.Event += Handler;
    obj.Event += Handler;
    obj.TriggerEvent();
    Console.WriteLine("---");
    obj.Event -= Handler;
    obj.TriggerEvent();
}

私が得る出力:

hi!
hi!
---
hi!

最後の「こんにちは!」まったく予想外でした。それを削除するには、Event -= Handler;もう一度電話する必要があります。しかし、ハンドラーがバインドされた回数がわからない場合はどうなりますか?

更新:この少し直感に反する動作の背後にある理由を知ることは興味深いでしょう:なぜ-=すべてのインスタンスを削除しないのですか?

更新2: jQueryとの違いにより、この動作は直感に反していることに気付きました。

var handler = function() { console.log('hi!'); }, obj = {};
$(obj).on("event", handler).on("event", handler).trigger("event");
console.log("---");
$(obj).off("event", handler).trigger("event");

出力:

hi!
hi!
---
4

5 に答える 5

2

なぜあなたの例が直感に反すると考えるのか理解できたと思います。

この変更を検討してください

var del = new Action(Handler);
obj.Event += del;
obj.Event += del;
obj.TriggerEvent();
Console.WriteLine("---");
obj.Event -= del;
obj.TriggerEvent();

それはあなたのものとまったく同じように機能しますが、なぜですか?

使用した場合

obj.Event += Handler

コンパイラはあなたの後ろで何かをしました。3回の新しいインスタンスを作成しましたAction(Handler)(2つ追加、1つ削除)。変更では、まったく同じデリゲートオブジェクトを使用します。

したがって、本当の質問は次のとおりです。あなたの例では、なぜ削除が機能したのですか?追加に使用されなかったオブジェクトを削除するために渡します。答えは、代表者は価値の平等を持っているということです。

var del1 = new Action(Handler);
var del2 = new Action(Handler);
Console.WriteLine("Reference equal? {0}, Value equal? {1}", Object.ReferenceEquals(del1, del2), del1.Equals(del2));
// Reference equal? False, Value equal? True

これで、「なぜ2つのイベントハンドラーが追加されたのですか?同じハンドラーであるため、1つだけにするべきではないのですか?」と考えるかもしれません。

答えはいいえだ"。マルチキャストデリゲートは、同じハンドラーを複数回追加してもかまいません。セットではなく、リストです。

1つのハンドラーを削除すると、リストに2つの同一のハンドラーがあることが認識され、そのうちの1つが削除されました。

于 2012-07-20T21:39:55.460 に答える
1

デリゲートは、割り当てたすべてのハンドラーを結合します。同じハンドラーを 2 回割り当てると、2 回呼び出されるため、2 回削除する必要があります。これは直感に反するとは思いません。

イベントを定義するクラスを制御できる場合は、次のようなものを使用して、特定のハンドラーのすべてのインスタンスを一度に削除できます。

private Action _Event;

public event Action Event
{
    add
    {
        _Event += value;
    }
    remove
    {
        while (_Event != null && _Event.GetInvocationList().Contains(value))
        {
            _Event -= value;
        }
    }
}

イベントを制御できない場合は、-=オペレーターがハンドラーのインスタンスを 1 つだけ削除することを受け入れる必要があります。これは言語の設計によるものであり、変更することはできません。

同じ文字列をList<string>複数回追加するようなものです。その文字列のすべてのインスタンスを削除する場合は、Remove メソッドを複数回呼び出す必要があります。

Fooクラスが他のクラスとは異なる動作をするため、クラスが他のユーザーによって使用される場合、上記のコードはお勧めしません。

于 2012-07-20T23:07:17.197 に答える
1

この解決策を試してくださいReflection を使用してイベント ハンドラーを削除する

また

 Delegate[] dellist = myEvent.GetInvocationList();
    foreach (Delegate d in v)
           myEvent-= (d as MyDelegate);//MyDelegate is type of delegate
于 2012-07-20T20:35:48.947 に答える
0

インスタンス フィールドがプロパティでラップされるように、デリゲートはイベントで「ラップ」する必要があります。その後、それらを制御できます。

public class Test
{
    public class Foo
    {
        private Action _event;
        public event Action Event
        {
            add { _event += value; }
            remove { _event -= value; }
        }

        public void DoEvent()
        {
            if (_event != null)
                _event ();
        }

        public void ClearEvent()
        {
            _event = null;
        }
    }

    static void Handler() {
        Console.WriteLine("hi!");
    }

    static void Main()
    {
        var foo = new Foo();

        foo.Event += Handler;
        foo.Event += Handler;
        foo.DoEvent();
        Console.WriteLine("---");

        foo.ClearEvent();

        foo.DoEvent();

        Console.Read();
    }
}
于 2012-07-20T20:35:03.100 に答える
-2
Event = Delegate.RemoveAll(Event, handler);

これはスレッドセーフではなく、イベントを宣言するクラス内でのみ機能することに注意してください。

于 2012-07-20T20:27:18.893 に答える