为什么从字典中获取时会得到 Action<> 的克隆?

2024-05-19

我有以下字典:

private Dictionary<string, Action<GameObject, int, int, int, int>> eventDictionary;

我希望保留一个操作字典(基本上是委托),以便每当我希望订阅事件名称时,我都可以为所有订阅者订阅相同的事件。

这是我监听某个事件字符串的函数:

public static void StartListening(string eventName, Action<GameObject, int, int, int, int> listener)
{
    Action<GameObject, int, int, int, int> thisEvent = null;
    if (instance.eventDictionary.TryGetValue(eventName, out thisEvent))
    {
        thisEvent += listener;
        // the code reaches this 
    }
    else
    {
        thisEvent += listener;
        instance.eventDictionary.Add(eventName, thisEvent);
    }
}

现在我尝试

EventManager.StartListening("Move", Moved);
EventManager.StartListening("Move", Moved);
EventManager.StartListening("Move", Moved);
EventManager.StartListening("Move", Moved);

// Log here to get how many subscribers there are to the event "Move"
// Result: only 1 listener

只有第一个添加的侦听器会实际注册,其余侦听器在添加后“消失”。我调试了这个错误近 4 个小时,最后测试看看是否可能是这条线thisEvent += listener;发生故障。当我添加删除并随后添加回字典时,

public static void StartListening(string eventName, Action<GameObject, int, int, int, int> listener)
{
    Action<GameObject, int, int, int, int> thisEvent = null;
    if (instance.eventDictionary.TryGetValue(eventName, out thisEvent))
    {
        thisEvent += listener;
        instance.eventDictionary.Remove(eventName);
        instance.eventDictionary.Add(eventName, thisEvent);
    }
    else
    {
        thisEvent += listener;
        instance.eventDictionary.Add(eventName, thisEvent);
    }
}

代表们终于如期加入了。

EventManager.StartListening("Move", Moved);
EventManager.StartListening("Move", Moved);
EventManager.StartListening("Move", Moved);
EventManager.StartListening("Move", Moved);

// Log here to get how many subscribers there are to the event "Move"
// Result: 4 listeners

这是我遇到过的最荒谬的错误之一。字典中不是字符串、整数等的所有值都应该通过引用而不是值来检索吗?为什么我在这里得到操作的克隆,而不是引用?

附: GameObject 是一个 Unity 类。 这是我的移动功能:

public void Moved(GameObject invoker, int x, int z, int tx, int tz)
{
    //Some code here
}

这是我遇到过的最荒谬的错误之一。字典中不是字符串、整数等的所有值都应该通过引用而不是值来检索吗?为什么我在这里得到操作的克隆,而不是引用?

你打电话时TryGetValue(eventName, out thisEvent)您正在提供一个参考Dictionary将写入该值。您没有获得对内部内容的引用Dictionary(我的意思是,你没有得到一个指向Dictionary结构,这意味着分配给它不会修改Dictionary).


也许一些混淆可能源于委托是引用类型这一事实。是的,您获得了对同一委托对象的引用,而不是新的委托对象。但是,委托添加会返回一个新的多播委托。

顺便一提,string是一个引用类型。

See:

  • 如何组合委托(多播委托)(C# 编程指南) https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/how-to-combine-delegates-multicast-delegates
  • + 运算符如何用于合并委托? https://stackoverflow.com/q/13549043/402022.

我只是想知道为什么要这样设计。几乎所有语言都有字典,可以在获取对象时为您提供参考。当我不确定对象是否为空时,我还应该如何获取对象?

内存安全。这种设计不会出现孤儿指针或类似的情况。

尽管如此,没有提供替代的更新 API 可以被视为一种疏忽,例如ConcurrentDictionary<TKey,TValue>.AddOrUpdate https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2.addorupdate?view=netframework-4.8. Dictionary并不意味着线程安全,你可以完成同样的事情,即使效率较低......这可以解释为什么它不被认为是必要的。

如果您正在寻找替代方案(除了ConcurrentDictionary)您可以使用可变引用类型。例如List<Delegate>例如。在TryGetValue(eventName, out thisEvent)您仍然会提供一个参考来编写List<Delegate>但是,您可以对其进行变异。但是,您仍然需要处理初始化它,当密钥不存在时您将执行此操作。你不会有空值。

if (instance.eventDictionary.TryGetValue(eventName, out var thisEvent))
{
    thisEvent.Add(listener);
}
else
{
    instance.eventDictionary.Add
    (
        eventName,
        new List<Action<GameObject, int, int, int, int> {listener}
    );
}

这也让我想知道,它是深克隆还是浅克隆?

如果您要存储值类型,您将获得该值类型的副本。

如果您要存储引用类型,您将获得引用的副本。我不会称之为克隆。它只是对同一对象的另一个引用。

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

为什么从字典中获取时会得到 Action<> 的克隆? 的相关文章

  • CMake 找不到请求的 Boost 库

    既然我已经浏览了其他人的解决方案几个小时 但找不到适合我的问题的正确答案 我想将我的具体问题带给您 我正在尝试使用 CMake 构建 vsomeip 为此 我之前构建了 boost 1 55 但是 我在 CMake 中收到以下错误 The
  • Poco c++Net:Http 从响应中获取标头

    我使用 POCO C Net 库进行 http 我想尝试制定持久缓存策略 首先 我认为我需要从缓存标头中获取过期时间 并与缓存值进行交叉检查 如果我错了 请告诉我 那么我如何从中提取缓存头httpResponse 我已经看到你可以用 Jav
  • 在路由mvc 4中添加公司名称

    我一直在尝试为 Facebook 等用户提供在 URL 中添加公司名称的选项 http localhost 50753 MyCompany Login 我尝试过不同的网址 但没有成功 routes MapRoute name Default
  • 解析 JWT 令牌以仅获取有效负载内容,无需 C# 或 Blazor 中的外部库

    我正在使用 Blazor 编写可以访问 JWT 的客户端应用程序 我想知道一种简单的方法来读取令牌有效负载内容而不添加额外的依赖项 因为我不需要其他信息 也不需要验证令牌 我认为解析有效负载内容应该足够简单 只需将其写入方法即可 JwtTo
  • 从代码中,如何创建对存储在附加属性中的对象的属性的绑定?

    我们有一个继承的附加属性来存储一个对象 在可视化树的更下方 我们希望从代码绑定到该对象的属性 通常我们像这样构建绑定的路径部分 var someBinding new Binding Path new PropertyPath Attach
  • 在开关中使用“goto”?

    我看到了一个建议的编码标准 内容如下Never use goto unless in a switch statement fall through 我不跟 这个 例外 案例到底是什么样的 这证明了goto 此构造在 C 中是非法的 swi
  • 将表(行)与 OpenXML SDK 2.5 保持在一起

    我想在 Word 文档中生成多个表 每行 2 行 但我想将这两行保留在一起 如果可能的话 new KeepNext 第一行不起作用 new KeepNext 第一行的最后一段不起作用 new CantSplit 放在桌子上不起作用 在所有情
  • 使用 LINQ 更新 IEnumerable 对象的简单方法

    假设我有一个这样的业务对象 class Employee public string name public int id public string desgination public int grade List
  • 使用查询表达式对 List 进行排序

    我在使用 Linq 订购这样的结构时遇到问题 public class Person public int ID get set public List
  • 增强精神、递归和堆栈溢出

    为什么下面的代码在运行时崩溃 它会给出堆栈溢出错误 include
  • 使用 C# 和 wpf 创建类似 Dock 的应用程序

    我需要创建一个与我们购买笔记本电脑时获得的应用程序类似的应用程序 仅当鼠标指针到达窗口顶部时它才可见 那么我怎样才能使用 C 4 0 来做到这一点呢 http www notebookcheck net uploads pics win2
  • 如何对 NServiceBus.Configure.WithWeb() 进行单元测试?

    我正在构建一个 WCF 服务 该服务接收外部 IP 上的请求并将其转换为通过 NServiceBus 发送的消息 我的单元测试之一调用Global Application Start 它执行应用程序的配置 然后尝试将 Web 服务解析为 验
  • 如何在三个 IEnumerable 上使用 Zip [重复]

    这个问题在这里已经有答案了 可能的重复 使用 Linq 从 3 个集合创建项目 https stackoverflow com questions 5284315 create items from 3 collections using
  • 搜索实体的所有字段

    我正在尝试在客户数据库上实现 多功能框 类型的搜索 其中单个查询应尝试匹配客户的任何属性 这是一些示例数据来说明我想要实现的目标 FirstName LastName PhoneNumber ZipCode Mary Jane 12345
  • 如何从 Rx Subscribe 回调异步函数?

    我想回调 Rx 订阅中的异步函数 例如 像那样 public class Consumer private readonly Service service new Service public ReplaySubject
  • 逆向工程 ASP.NET Web 应用程序

    我有一个 ASP NET Web 应用程序 我没有源代码 该 bin 包含 10 个程序集和一个 compiled 文件 我在 App Code dll 上使用 Reflector 它向我显示了类和命名空间之类的东西 但它太混乱了 有没有什
  • 在 C#.NET 中安全删除文件

    在我正在做的一个项目中 我想为用户提供 安全 删除文件的选项 例如 用随机位或 0 覆盖它 在 C NET 中是否有一种简单的方法可以做到这一点 效果如何 你可以调用系统内部删除 http technet microsoft com en
  • 如何调试 .NET 运行时中的内部错误?

    我正在尝试调试一些处理大文件的工作 代码本身works 但 NET 运行时本身会报告零星错误 对于上下文 这里的处理是一个 1 5GB 文件 仅加载到内存中一次 在循环中处理和释放 故意尝试重现此否则不可预测的错误 我的测试片段基本上是 t
  • 需要提取字符串中点后的最后一个数字,如“7.8.9.1.5.1.100”

    我需要提取 C 字符串中最后一个点后面的最后一个数字 例如 7 8 9 1 5 1 100 并将其存储在整数中 Added 该字符串也可以是 7 8 9 1 5 1 1 或 7 8 9 1 5 1 0 我还想验证它在最后一个点之前恰好是 7
  • 使用 using 声明时,非限定名称查找如何工作?

    根据 C 标准 这是格式错误还是格式良好 namespace M struct i namespace N static int i 1 using M i using N i int main sizeof i Clang 拒绝它 GCC

随机推荐