为什么当发送者不是名义发送者时,WeakEventManager 不会触发事件?

2024-01-14

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

考虑一个公开事件的普通类,这里是非常常见的 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源代码 http://reflector.webtropy.com/default.aspx/4@0/4@0/DEVDIV_TFS/Dev10/Releases/RTMRel/wpf/src/Base/System/Windows/WeakEventManager@cs/1305600/WeakEventManager@cs看看为什么会发生这种情况。

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

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


Edit

下面是一些伪代码来说明。

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参数现在不同了!因此它将无法检索处理程序,并且不会调用任何内容。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么当发送者不是名义发送者时,WeakEventManager 不会触发事件? 的相关文章

随机推荐

  • 使用 bash、sed 或 awk 拆分 CSV 文件并排除输出中的列

    我有一个 CSV 文件 其中包含如下数据 1 275 275 17 3 0 2011 05 09 20 21 45 2 279 279 17 3 0 2011 05 10 20 21 52 3 276 276 17 3 0 2011 05
  • 如何在 vscode 中将 .ipynb 作为常规文本而不是 IPython 笔记本查看

    VS Code 最近添加了使用 Jupyter Notebooks 的功能 ipynb文件 这就是说它将它们呈现为笔记本而不是底层文本 我遇到的问题是我想对原始文本而不是 笔记本 进行一些编辑 但我不知道如何像其他编辑器那样将其显示为文本文
  • git push -f 与 +

    这样做有什么区别 git push f origin my branch my branch and git push origin my branch my branch 这是实现同一目标的两种语法 除了那个git push force当
  • 我可以从视图调用模型吗?

    我没有使用成熟的 PHP MVC 而是设计了一个最适合我的用途的 MVC 我已经完成了基本框架 并编写了运行网站所需的模型和控制器 现在我正在转向视图 我遇到了一个小困境 我的方法对我来说效果很好 但为了将来的参考 我想知道我所做的是否是一
  • Django 的 CreateView 不保存对象

    我正在练习 Django 的通用视图 特别是ModelForms 这些是我的观点和模型 模型 py class Post models Model user models ForeignKey User post title models
  • 为什么我使用kill -0 (|pid) 得到不同的结果

    我正在编写一个脚本 该脚本将启动另一个进程 并在一段时间后杀死它 如果它没有自行退出 自从开始这件事以来 我开始意识到timeout命令是执行此操作的正确方法 但我仍然对为什么我的方法之一失败感到困惑 这就是我正在尝试的 bin bash
  • JS 在循环内等待回调完成

    我的 Node js 应用程序中有一个 for 循环 在此循环内 每次迭代都可以执行 mysql 查询 并非总是如此 具体取决于情况 查询是异步的 我在成功回调中得到结果 但我需要 for 循环的每次迭代都等待回调完成 如果需要 funct
  • Mongodb TTL 使文档提前过期

    我正在尝试将文档插入 Mongo 数据库并让它在预定时间后自动过期 到目前为止 我的文档已被插入 但总是会在 0 60 秒内从数据库中删除 即使我将 expireAfterSeconds 设置得更长 我知道 mongodb 大约每 60 秒
  • 具有重叠时隙的会议调度算法

    我想做类似的事情预约调度算法 N个人 N个忙闲时段 约束满足 https stackoverflow com questions 11143439 appointment scheduling algorithm n people with
  • 在Python中将秒转换为周-天-小时-分钟-秒

    我正在尝试编写一个 输入秒数 的 Python 脚本 并在周 天 小时 分钟和秒内获得结果 这是我所拥有的 但我没有得到正确的答案 我究竟做错了什么 seconds raw input Enter the number of seconds
  • Firebase 上 iOS 的 Flutter 集成测试?

    是否可以在 Firebase 上运行 Flutter 集成测试 这方面的信息似乎相互矛盾 一些消息来源说这是可能的 但文档 https pub dev packages integration test firebase test lab
  • 将 wpf 图像控件保存到文件的最简单方法

    我的 wpf 应用程序中有一个 Image 控件 里面有一个大图像 但控件本身只有 60x150 这意味着它只显示该图像的特定部分 将可见部分保存到文件的最简单方法是什么 感谢您的帮助 编辑 我最终使用了找到的代码here http nui
  • Fabric - 在所有任务完成之前和之后在本地运行命令

    我试图在我的结构脚本中宣布部署开始和结束 感觉这应该很容易 但对于我来说 我不知道该怎么做 env hosts www1 www2 www3 www4 def announce start code to connect to irc se
  • 关于从 VBScript 返回值到 .NET 的问题

    我正在尝试设置一个能够从 NET 运行 VBScript 文件的应用程序 请参阅here https stackoverflow com questions 2886155 how do i get back results running
  • 如何在discord.py 中设置可选参数?

    我尝试过在线研究 但没有其他问题能够帮助我解决问题 这是我的场景 我正在discord py 中创建静音命令 我希望时间是可选的 但如果未指定时间 我希望该参数成为原因的一部分 澄清我的意思 client command commands
  • 如何将应用程序日志动态输出到TextArea和文本文件?

    我有一个 javaFX 应用程序 但遇到日志记录问题 我不知道如何将日志动态输出到 JavaFX 中的 TextArea 并同时输出到文件 对于文件我有这样的设置 handlers java util logging FileHandler
  • jQuery 语法 - 何时使用 $(美元)与 jQuery [重复]

    这个问题在这里已经有答案了 这两者有什么区别 SPANID html Some Text jQuery SPANID html Some Text 它是原型还是 jQuery 他们都做同样的事情 大多数库使用 作为访问库内函数的更短方式 j
  • 设置标签属性并使用 nokogiri builder (ruby) 将纯文本内容添加到标签

    我正在尝试使用 Nokogiri 和一些标签构建 XML 这些标签内既有属性又有纯文本 所以我试图做到这一点
  • 关于 Gatsby 页面加载网络请求的澄清

    我正在学习 Gatsby 的工作原理 并且有一个有 2 个页面的网站 两个页面都有彼此的超链接 由于 gatsby 使用 javascript 捆绑包将网站编译为静态页面 因此我希望 html 链接单击事件触发对服务器的页面请求以加载sec
  • 为什么当发送者不是名义发送者时,WeakEventManager 不会触发事件?

    我不喜欢非标准模式 但我正在对我的应用程序进行快速测试 并且遇到了这种奇怪的行为 考虑一个公开事件的普通类 这里是非常常见的 PropertyChanged 但我认为可能是任何其他事件 订阅者选择通过 WeakEventManager 帮助