为什么当发送方不是标称发送方时,WeakEventManager不触发事件



我不喜欢脱离标准模式,但我在我的应用程序上进行了一个快速测试,我遇到了这种奇怪的行为。

考虑一个公开事件的普通类,这里是非常常见的PropertyChanged,但我认为可以是任何其他类。

订阅者选择通过WeakEventManager帮助程序订阅事件。现在,"奇怪"的是实际的发送方引用:只要实例与订阅中使用的实例相同,一切都会好起来。但是,当您使用另一个对象时,将不会发出任何通知。

同样,NOT是一个好模式,但我想知道是否有任何好的理由造成这种限制,或者更确切地说,这是一种错误。与其说是真正的需要,不如说是一种好奇心。

class Class1
{
    static void Main(string[] args)
    {
        var c = new MyClass();
        WeakEventManager<INotifyPropertyChanged, PropertyChangedEventArgs>.AddHandler(
            c,
            "PropertyChanged",
            Handler
            );
        c.ActualSender = c;
        c.Number = 123;  //will raise
        c.ActualSender = new Class1();
        c.Number = 456;  //won't raise
        Console.ReadKey();
    }
    static void Handler(object sender, PropertyChangedEventArgs e)
    {
        Console.WriteLine("Handled!");
    }
}
class MyClass : INotifyPropertyChanged
{
    public object ActualSender { get; set; }

    private int _number;
    public int Number
    {
        get { return this._number; }
        set
        {
            if (this._number != value)
            {
                this._number = value;
                this.OnPropertyChanged("Number");
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(
        string name
        )
    {
        this.PropertyChanged(
            this.ActualSender, 
            new PropertyChangedEventArgs(name)
            );
    }
}

编辑:这里有一个实现预期行为的粗略方法(为了简单起见,硬链接)。

class Class1
{
    static void Main(string[] args)
    {
        var cx = new MyClass();
        var cy = new MyClass();
        Manager.AddHandler(cx, Handler1);
        Manager.AddHandler(cx, Handler2);
        Manager.AddHandler(cy, Handler1);
        Manager.AddHandler(cy, Handler2);
        cx.ActualSender = cx;
        cx.Number = 123;
        cx.ActualSender = new Class1();
        cx.Number = 456;
        cy.ActualSender = cy;
        cy.Number = 789;
        cy.ActualSender = new Class1();
        cy.Number = 555;
        Console.ReadKey();
    }
    static void Handler1(object sender, PropertyChangedEventArgs e)
    {
        var sb = new StringBuilder();
        sb.AppendFormat("Handled1: {0}", sender);
        var c = sender as MyClass;
        if (c != null) sb.AppendFormat("; N={0}", c.Number);
        Console.WriteLine(sb.ToString());
    }
    static void Handler2(object sender, PropertyChangedEventArgs e)
    {
        var sb = new StringBuilder();
        sb.AppendFormat("Handled2: {0}", sender);
        var c = sender as MyClass;
        if (c != null) sb.AppendFormat("; N={0}", c.Number);
        Console.WriteLine(sb.ToString());
    }
}
static class Manager
{
    private static Dictionary<object, Proxy> _table = new Dictionary<object, Proxy>();
    public static void AddHandler(
        INotifyPropertyChanged source,
        PropertyChangedEventHandler handler
        )
    {
        var p = new Proxy();
        p._publicHandler = handler;
        source.PropertyChanged += p.InternalHandler;
        _table[source] = p;
    }
    class Proxy
    {
        public PropertyChangedEventHandler _publicHandler;
        public void InternalHandler(object sender, PropertyChangedEventArgs args)
        {
            this._publicHandler(sender, args);
        }
    }
}

我还没有找到任何说明这一点的文档,但您可以查看WeakEventManager源代码来了解为什么会发生这种情况。

管理器保留一个表,将已注册的源对象映射到其处理程序。请注意,这个源对象是您在添加处理程序时传入的对象。

当管理器接收到事件时,它会使用事件的发送方作为关键字,从该表中查找相关的处理程序。显然,如果这个发送方与注册的发送方不同,那么将找不到预期的处理程序。


编辑

下面是一些要说明的伪代码。

public class PseudoEventManager : IWeakEventListener
{
    private static PseudoEventManager _instance = new PseudoEventManager();
    private readonly Dictionary<object, List<object>> _handlerTable 
                             = new Dictionary<object, List<object>>();
    public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
    {
        foreach (var handler in _handlerTable[sender]) // point of interest A
            //invoke handler
    }
    public static void AddHandler(object source, object handler)
    {
        if (!_instance._handlerTable.ContainsKey(source)) 
            _instance._handlerTable.Add(source, new List<object>()); //point of interest B
        _instance._handlerTable[source].Add(handler);
        //attach to event
    }
}

添加处理程序时,传入的源将添加到查找表中。当接收到事件时,将查询此表中该事件的发送方,以获取该发送方/源的相关处理程序。

在您的示例中,您正在侦听的源是c,这也是第一次ActualSender的值。因此,事件的发送方与注册的源相同,这意味着正确地找到并调用了处理程序。

然而,第二次,ActualSender是与c不同的实例。注册的源没有更改,但sender参数的值现在不同了!因此,它将无法检索处理程序,也不会调用任何东西。

相关内容

  • 没有找到相关文章