4

How do I unsubscribe from an event of a generic class whose type parameter is specified within a generic method as follows?

public class ListLayoutControl : Control
{
    NotifyCollectionChangedEventHandler handler = null;

    public void AttachTo<T, U>(T list) where T : INotifyCollectionChanged, ICollection<U>
    {
        handler = delegate (object sender, NotifyCollectionChangedEventArgs e)
        {
            UpdateLayout(list.Count);
        };
        list.CollectionChanged += handler;
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            // unsubscribe ??
        }
        base.Dispose(disposing);
    }
}
Ozgur Ozcitak
  • 10,409
  • 8
  • 46
  • 56
  • The problem here is I can't keep a reference to the generic class since its type parameter is defined inside the generic method. So there is no reference to unsubscribe from. Whether I use a lambda or delegate doesn't matter. – Ozgur Ozcitak Apr 03 '18 at 14:03
  • Edited to use a delegate to clarify my point. – Ozgur Ozcitak Apr 03 '18 at 14:09
  • @Sinatr Whether you use an anonymous method or a lambda makes no difference. Using an anonymous method is merely a more verbose and antiquated syntax for doing the same thing, and has no impact on your ability to unsubscribe from the event. You simply linked to a question/answer that was posted *before lambdas existed*, which is why it uses anonymous methods. – Servy Apr 03 '18 at 14:23
  • @Sinatr Yes, I'm aware of that, and hence I responded to your completely incorrect statement that this is any different with a lambda than with an anonymous method. Solving this problem is no different with a lambda than with an anonymous method. You got the OP to edit the question to only ever made their code use an antiquated syntax for accomplishing what they're doing that has no advantages in the situation and is just more verbose, unnecessarily. – Servy Apr 03 '18 at 14:57
  • @Sinatr That answer would work *identically* if you used a lambda instead of an anonymous method. Your statement that you cannot use a lambda is simply false. You most certainly can. Yes, you'd need to store that lambda in a delegate (which the original revision of the question was already doing), but you can do that just as easily with a lambda as you can with an anonymous method (again, as seen by the first revision of the question). I'm aware that your comment wasn't an answer, it was simply an incorrect statement. – Servy Apr 03 '18 at 15:08
  • @Sinatr The statement isn't ambiguous, it's wrong. You said that they can't use a lambda if they want to unsubscribe from the event, but you can. A "lambda" is not the term for using a lambda to subscribe to an event without storing the resulting delegate in a second location first. The term "lambda" refers to the specific syntax for creating a method without a name. Which syntax you use for creating a method without a name has no effect on your ability to unsubscribe it from an event, all that matters is whether you store the resulting delegate somewhere, which the OP did. – Servy Apr 03 '18 at 15:28
  • @Servy, "don't use lambda without reference if you want to unsubscribe later". Or make it even better. Then make same mistake as I, call **that** long thing simply - lambda. That's what I did. And I hope I explained it well. Now you don't like another statement - ambitious. This can continue forever because: 1) you speak better 2) you have better C# knowlege. But should we? I am going to delete my comments shortly. – Sinatr Apr 03 '18 at 15:31
  • @Sinatr But the OP was *already* storing the lambda in a delegate field, even in the original revision. So they weren't even doing the thing you were trying to tell them to not do. And no, you cannot just say "lambda" to refer to using a lambda without storing the result in a variable. That's just not what the word means (and the OP wasn't even doing that in the original revision). If you want to make up a term for that quote, then don't use an existing term that means something very different. Something being ambiguous means it could be interpreted to mean different things. – Servy Apr 03 '18 at 15:39
  • @Servy, *"even in the original revision"* - that's wrong [for sure](https://stackoverflow.com/revisions/49631952/1). – Sinatr Apr 04 '18 at 06:49

1 Answers1

4

Capture unsubscribe in separate delegate and execute it on dispose:

private Action _unsubscribeHandler;
public void AttachTo<T, U>(T list) where T : INotifyCollectionChanged, ICollection<U>
{
    NotifyCollectionChangedEventHandler handler = delegate (object sender, NotifyCollectionChangedEventArgs e)
    {
        UpdateLayout(list.Count);
    };
    list.CollectionChanged += handler;
    _unsubscribeHandler = () => {
        list.CollectionChanged -= handler;     
    };
}

protected override void Dispose(bool disposing)
{
    if (disposing)
    {
        _unsubscribeHandler?.Invoke();
    }
    base.Dispose(disposing);
}

If it's possible to call AttachTo multiple times with different lists - collect unsubscribe handlers in List<Action> and on dispose execute them all.

Evk
  • 98,527
  • 8
  • 141
  • 191