我有以下字典:
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
这是我遇到过的最荒谬的错误之一。难道不是字典中不是字符串,整数等所有值都应该通过引用而不是值来检索吗?为什么我在这里获得Action的副本,而不是参考?
PS: GameObject是Unity类。 这是我的Moved函数:
public void Moved(GameObject invoker, int x, int z, int tx, int tz)
{
//Some code here
}
答案 0 :(得分:3)
这是我遇到过的最荒谬的错误之一。难道不是字典中不是字符串,整数等所有值都应该通过引用而不是值来检索吗?为什么我在这里获得Action的副本,而不是参考?
调用TryGetValue(eventName, out thisEvent)
时,将提供Dictionary
写入值的引用。您没有得到Dictionary
内部内容的引用(我的意思是,您没有得到Dictionary
结构的深层指针,这意味着分配给它不会修改Dictionary
)。
也许委托可能是一个引用类型,这可能引起一些混淆。 是的,您得到的是对同一委托对象的引用,而不是新的引用。但是,委托添加会返回一个新的多播委托。
顺便说一句,string
是引用类型。
请参阅:
我只是想知道为什么要这样设计。几乎所有语言都有字典,可在获取对象时为您提供参考。当我不确定是否为null时,应该如何获取对象?
内存安全。这种设计将不可能有孤立的指针或类似的指针。
尽管如此,如果没有替代的更新API,例如ConcurrentDictionary<TKey,TValue>.AddOrUpdate
,也可以视为一种疏忽。 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}
);
}
这也让我感到疑惑,它是深克隆还是浅克隆?
如果要存储值类型,则将获得该值类型的副本。
如果要存储引用类型,则将获得引用的副本。我不会称其为克隆。它只是对同一对象的另一个引用。