提问人:Tea 提问时间:11/17/2022 最后编辑:Tea 更新时间:11/17/2022 访问量:76
如何更新从 TryGetValue 返回的引用后将其插入集合中?
How can I update a reference returned from TryGetValue after its been inserted into a collection?
问:
我在字典里有一本字典。我想将对内部字典的引用设置为一个值,将其添加到外部字典中,如下所示:
var mammalIdSubscribers = new Dictionary<int, Dictionary<Guid, int>>();
var mammalId = 0;
if (!mammalIdSubscribers.TryGetValue(mammalId, out var mammalSubscribers))
mammalIdSubscribers[mammalId] = mammalSubscribers; // Add reference to inner dict to outer dict
Subscribe(ref mammalSubscribers);
/*
mammalIdSubscribers[mammalId] is still null after the call
to Subscribe despite mammalSubscribers being non-null. Why?
*/
static void Subscribe(ref Dictionary<Guid, int> subscribers)
{
subscribers = new Dictionary<Guid, int> { { Guid.NewGuid(), 10 } };
}
不幸的是,这不起作用,我不确定为什么(抛出空引用异常)。 Console.WriteLine(mammalSubscribers.First().Value);
有人可以解释为什么这不起作用吗?换句话说,为什么仍然在调用之后使用关键字?mammalIdSubscribers[0]
null
Subscribe
ref
答:
1赞
Dai
11/17/2022
#1
您的变量和 的名称非常相似,因此为了清楚起见,我将重命名为“”或“biggerDict
”,同时重命名为“encarta
”,因为我经常将其用作参考。mammalIdSubscribers
mammalSubscribers
mammalIdSubscribers
outerDict
mammalSubscribers
逐行...
var biggerDict = new Dictionary<int, Dictionary<Guid, int>>();
- 这给了我们一个有效但空洞的字典。
biggerDict
- 这给了我们一个有效但空洞的字典。
var mammalId = 0;
- 明显。
biggerDict.TryGetValue(mammalId, out var encarta)
- 其计算结果为 。参数也是一个内联声明,当您使用带有 's 的内联声明时,新变量在返回时将是 (或 )。
false
out
out
out
Dictionary
TryGetValue
null
default
false
- ...它将返回,因为如前所述是空的。
false
biggerDict
- ...因此是 .
encarta
null
- (如果你眨了眨眼,错过了它:
encarta
是堆栈上一个新的 GC 引用类型变量,它不是biggerDict
任何部分的别名或“引用”)。
- 其计算结果为 。参数也是一个内联声明,当您使用带有 's 的内联声明时,新变量在返回时将是 (或 )。
- 因为调用在语句中,所以意味着将对其进行计算。
TryGetValue
if( !TryGetValue(...) )
biggerDict[mammalId] = encarta;
- 并且仍然是.
encarta
null
- ...因此(又名)是 。
biggerDict[mammalId]
biggerDict[0]
null
- 并且仍然是.
Subscribe(ref encarta);
- 这会将对局部变量的引用传递给 。
encarta
Subscribe
- 至关重要的是,它不是对任何插槽或空间的引用:它仍然只是一个堆栈分配的(又名自动的)对象引用大小的插槽。
encarta
biggerDict
null
- 这会将对局部变量的引用传递给 。
encarta = new Dictionary<Guid, int> { { Guid.NewGuid(), 10 } };
- 在内部,在机器语言级别,指向堆栈分配的本地的指针 (-ish) 被尊重并分配给该 。
Subscribe
encarta
new Dictionary<Guid, int> { { Guid.NewGuid(), 10 } };
- ...这意味着现在不是
null
。encarta
- 然后执行返回到上一个函数。
- 在内部,在机器语言级别,指向堆栈分配的本地的指针 (-ish) 被尊重并分配给该 。
- local 现在是对 GC 堆上该有效字典对象的引用。但是从来没有调用
过 biggerDict[int].set_Item
属性 setter 来使biggerDict[0]
成为对encarta
指向的同一对象的非null
引用。encarta
- 请记住,除了实数组 () 之外,所有其他带有索引器的类型都只是对属性 getter/setter 方法的糖,这意味着对象引用是通过值传递的,而不是 references-passed-by-reference 传递的 - 至少没有 -return 属性,这是不行的。
T[]
ref
Dictionary<K,V>
评论
Dictionary
不会公开其内容以供引用访问,即您无法进入其内部哈希表桶之一。您只能对公开属性的类型执行此操作,并且必须使用: learn.microsoft.com/en-us/dotnet/csharp/language-reference/... - 另请参阅运算符的文档:learn.microsoft.com/en-us/dotnet/csharp/language-reference/...ref
public ref...
ref local
= ref
Console.Log
mammalSubscribers