为什么WeakEventManager在发件人不是名义上时不会触发事件?

我不喜欢不合标准的模式,但我正在对我的应用程序进行快速测试,我遇到了这种奇怪的行为。

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

订户选择通过WeakEventManager帮助程序订阅事件。 现在,“奇怪”的东西是实际的发送者引用:只要实例与订阅上使用的实例相同,一切都很顺利。 但是,当您使用其他对象时,不会发出通知。

同样,这不是一个好的模式,但我想知道这个限制是否有任何好的理由,或者说这是一种错误。 更多的是好奇心而不是真正的需求。

class Class1 { static void Main(string[] args) { var c = new MyClass(); WeakEventManager.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 _table = new Dictionary(); 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> _handlerTable = new Dictionary>(); 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()); //point of interest B _instance._handlerTable[source].Add(handler); //attach to event } } 

添加处理程序时,传入的源将添加到查找表中。 收到事件后,将为此事件的发件人查询此表,以获取此发件人/来源的相关处理程序。

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

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