239

イベントから匿名メソッドのサブスクライブを解除することは可能ですか?

このようなイベントにサブスクライブすると:

void MyMethod()
{
    Console.WriteLine("I did it!");
}

MyEvent += MyMethod;

次のように登録解除できます。

MyEvent -= MyMethod;

しかし、匿名の方法を使用してサブスクライブすると、次のようになります。

MyEvent += delegate(){Console.WriteLine("I did it!");};

この匿名メソッドの登録を解除することはできますか? もしそうなら、どのように?

4

13 に答える 13

239
Action myDelegate = delegate(){Console.WriteLine("I did it!");};

MyEvent += myDelegate;


// .... later

MyEvent -= myDelegate;

デリゲートへの参照を保持するだけです。

于 2008-10-08T15:33:38.177 に答える
149

1 つの手法は、匿名メソッドを保持する変数を宣言して、匿名メソッド自体の内部で使用できるようにすることです。イベントが処理された後に購読を解除することが望ましい動作だったので、これは私にとってはうまくいきました。

例:

MyEventHandler foo = null;
foo = delegate(object s, MyEventArgs ev)
    {
        Console.WriteLine("I did it!");
        MyEvent -= foo;
    };
MyEvent += foo;
于 2008-10-08T15:34:51.497 に答える
24

メモリから、仕様は、匿名メソッドで作成されたデリゲートの同等性に関しては、どちらの方法でも動作を明示的に保証しません。

サブスクライブを解除する必要がある場合は、「通常の」方法を使用するか、デリゲートを別の場所に保持して、サブスクライブに使用したのとまったく同じデリゲートでサブスクライブを解除できるようにする必要があります。

于 2008-10-08T15:26:56.067 に答える
17

3.0 では、次のように短縮できます。

MyHandler myDelegate = ()=>Console.WriteLine("I did it!");
MyEvent += myDelegate;
...
MyEvent -= myDelegate;
于 2009-07-23T01:53:51.303 に答える
11

デリゲートへの参照を保持する代わりに、イベントの呼び出しリストを呼び出し元に返すためにクラスをインストルメント化できます。基本的に、次のように記述できます (MyEvent が MyClass 内で宣言されていると仮定します)。

public class MyClass 
{
  public event EventHandler MyEvent;

  public IEnumerable<EventHandler> GetMyEventHandlers()  
  {  
      return from d in MyEvent.GetInvocationList()  
             select (EventHandler)d;  
  }  
}

そのため、MyClass の外部から呼び出しリスト全体にアクセスし、必要なハンドラーのサブスクライブを解除できます。例えば:

myClass.MyEvent -= myClass.GetMyEventHandlers().Last();

このテクニックに関する完全な記事をここに書きました。

于 2011-06-23T23:11:50.210 に答える
6

不自由なアプローチの種類:

public class SomeClass
{
  private readonly IList<Action> _eventList = new List<Action>();

  ...

  public event Action OnDoSomething
  {
    add {
      _eventList.Add(value);
    }
    remove {
      _eventList.Remove(value);
    }
  }
}
  1. イベントの追加/削除メソッドをオーバーライドします。
  2. これらのイベント ハンドラーのリストを保持します。
  3. 必要に応じて、それらをすべてクリアし、他のものを再度追加します。

これは機能しないか、最も効率的な方法である可能性がありますが、仕事は完了するはずです。

于 2008-10-08T15:29:04.330 に答える
2

購読解除を制御できるようにしたい場合は、受け入れられた回答に示されているルートに進む必要があります。ただし、サブスクライブクラスがスコープ外になったときに参照をクリアすることだけを懸念している場合は、弱参照を使用する別の(少し複雑な)ソリューションがあります。このトピックに関する質問と回答を投稿しました。

于 2009-11-17T08:05:49.073 に答える
0

このデリゲートでいくつかのオブジェクトを参照したい場合は、Delegate.CreateDelegate(Type, Object target, MethodInfo methodInfo) .net を使用できます。デリゲートは target と methodInfo で等しいと見なします

于 2014-01-21T03:19:30.787 に答える
0

サブスクライブされた eventHandler で参照を保持することが最善の方法である場合、これは Dictionary を使用して実現できます。

この例では、匿名メソッドを使用して、一連の DataGridViews の mergeColumn パラメーターを含める必要があります。

enable パラメーターを true に設定して MergeColumn メソッドを使用すると、イベントが有効になり、false で使用するとイベントが無効になります。

static Dictionary<DataGridView, PaintEventHandler> subscriptions = new Dictionary<DataGridView, PaintEventHandler>();

public static void MergeColumns(this DataGridView dg, bool enable, params ColumnGroup[] mergedColumns) {

    if(enable) {
        subscriptions[dg] = (s, e) => Dg_Paint(s, e, mergedColumns);
        dg.Paint += subscriptions[dg];
    }
    else {
        if(subscriptions.ContainsKey(dg)) {
            dg.Paint -= subscriptions[dg];
            subscriptions.Remove(dg);
        }
    }
}
于 2016-10-13T06:40:33.820 に答える
0

ラムダ式の代わりにクロージャーを自分で実装することで、これを解決する方法があります。

キャプチャ変数として使用するクラスは次のとおりであるとします。

public class A
{
    public void DoSomething()
    {
        ...
    }
}

public class B
{
    public void DoSomething()
    {
        ...
    }
}

public class C
{
    public void DoSomething()
    {
        ...
    }
}

これらのクラスはキャプチャ変数として使用されるため、インスタンス化します。

A a = new A();
B b = new B();
C c = new C();

以下に示すように、クロージャ クラスを実装します。

private class EventHandlerClosure
{
    public A a;
    public B b;
    public C c;

    public event EventHandler Finished;

    public void MyMethod(object, MyEventArgs args)
    {
        a.DoSomething();
        b.DoSomething();
        c.DoSomething();
        Console.WriteLine("I did it!");

        Finished?.Invoke(this, EventArgs.Empty);
    }
}

クロージャー クラスをインスタンス化し、ハンドラーを作成してから、イベントをサブスクライブし、クロージャー クラスの Finished イベントからサブスクライブを解除するラムダ式をサブスクライブします。

var closure = new EventHandlerClosure
{
    a = a,
    b = b,
    c = c
};
var handler = new MyEventHandler(closure.MyMethod);
MyEvent += handler;
closure.Finished += (s, e)
{
    MyEvent -= handler;
}
于 2021-04-10T22:23:32.330 に答える