为什么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
添加处理程序时,传入的源将添加到查找表中。 收到事件后,将为此事件的发件人查询此表,以获取此发件人/来源的相关处理程序。
在您的示例中,您正在侦听的源是c
,这是第一次也是ActualSender
的值。 因此,事件的发送者与注册的源相同,这意味着正确地找到并调用了处理程序。
但是,第二次, ActualSender
是一个与c
不同的实例。 注册的源不会更改,但sender
参数的值现在不同了! 因此,它将无法检索处理程序,也无法调用任何内容。