代码之家  ›  专栏  ›  技术社区  ›  Alex J

设置属性时交换事件的最优雅方式是什么?

  •  1
  • Alex J  · 技术社区  · 15 年前

    public class SomeClass
    {
        SomeOtherClass foo;
    
        public SomeOtherClass Foo
        {
            get { return foo; }
            set {
                if (value != foo) {
                    if (value != null) {
                        // subscribe to some events
                        value.SomeEvent += foo_SomeEvent;
                    }
    
                    if (foo != null) {
                        // unsubscribe from subscribed events
                        foo.SomeEvent -= foo_SomeEvent;
                    }
    
                    foo = value;
                }
            }
        }
    
        void foo_SomeEvent(object sender, EventArgs e)
        {
            // do stuff
        }
    }
    

    (当然,如果 foo 是不可变的,但这样我就得不到任何可视化设计器支持。)

    3 回复  |  直到 15 年前
        1
  •  5
  •   Reed Copsey    15 年前

    我确实认为你目前的实施是完全可以接受的。它非常清晰,易于理解。


    private void SwapEventHandler<T>(T a, T b, Action<T> subscribe, Action<T> unsubscribe)
        where T : class
    {
        if (a != null) 
            subscribe(a);
        if (b != null)
            unsubscribe(b);
    }
    

    然后你可以写:

    if (value != foo) 
    {
        SwapEventHandler(value,foo, (o) => o.SomeEvent += foo_SomeEvent, (o) => o.SomeEvent -= foo_SomeEvent );
        foo = value;
    }
    
        2
  •  1
  •   Jason Williams    15 年前

    您所做的很好,但我通常更喜欢取消订阅旧事件处理程序的约定 订阅新的事件,只是为了避免任何潜在的“重叠”,如果在调用之间从另一个线程触发相同的事件,两个对象可能会尝试处理相同的事件。

    对于边际改进,您可以省去不必要的大括号,使代码更加紧凑和“整洁”(旁观者眼中的“整洁”)。

    set
    {
        if (value != foo)
        {
            if (foo != null)
                foo.SomeEvent -= foo_SomeEvent;
            if (value != null)
                value.SomeEvent += foo_SomeEvent;
    
            foo = value;
        }
    }
    

    如果您将使用空引用定为非法(例如,通过使用对“空Foo对象”的引用而不是空引用),那么您可以完全免除ifs:

    set
    {
        if (value != foo)
        {
            foo.SomeEvent -= foo_SomeEvent;
            value.SomeEvent += foo_SomeEvent;
            foo = value;
        }
    }
    

    set
    {
        Helpers.SetValueAndResubscribeFooSomeEvent(ref foo, value);
    }
    
        3
  •  0
  •   Lars Udengaard    15 年前

    如果您不知道已在事件上注册了哪些事件,并且希望完全清除它,则可以执行以下操作:

    public class SomeOtherClass
    {
        public event EventHandler SomeEvent;
    
        public void ClearSomeEvent()
        {
            foreach (EventHandler e in SomeEvent.GetInvocationList())
            {
                SomeEvent -= e;
            }
        }
    }
    

    在SomeClass.Foo属性设置器中:

    if (foo != null)
    {
        // unsubscribe from subscribed events                    
        foo.ClearSomeEvent();                
    }
    

    如果您确实知道被订阅的代理,那么您当前的解决方案很好。