我如何更新从TryGetValue被插入到集合后返回的引用?



我有一个字典里面的字典。在添加到外部字典之后,我想将对内部字典的引用设置为值:

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);抛出空引用异常)。

有人能解释一下为什么这不起作用吗?换句话说,为什么用ref关键字调用Subscribe后,mammalIdSubscribers[0]仍然是null?

您的变量mammalIdSubscribersmammalSubscribers的命名非常相似,因此为了清晰起见,我将mammalIdSubscribers重命名为"outerDict";或者是"biggerDict";而mammalSubscribers被重命名为"encarta",因为我使用作为参考作为一个spprog。

逐行……

  1. var biggerDict = new Dictionary<int, Dictionary<Guid, int>>();
    • 这给了我们一个有效的,但是空的biggerDict字典。
  2. var mammalId = 0;
    • 不言自明。
  3. biggerDict.TryGetValue(mammalId, out var encarta)
    • 计算结果为falseout参数也是一个内联out声明,当你使用内联out声明与DictionaryTryGetValue,那么新的变量将是null(或default),当它返回false
    • …并且它将返回false,因为biggerDict是空的,如前所述。
    • …因此encarta就是null
    • (如果你眨眼错过了它:encarta是堆栈上的一个新的GC引用类型变量,它是而不是别名或"引用";到biggerDict的任何部分)。
  4. 因为TryGetValue调用在if( !TryGetValue(...) )语句中,这意味着biggerDict[mammalId] = encarta;将被求值。
    • encarta仍然是null
    • …因此biggerDict[mammalId](又名biggerDict[0])是null
  • Subscribe(ref encarta);
    • 将本地变量encarta的引用传递给Subscribe
    • 至关重要的是encartabiggerDict中的任何插槽或空间的引用:它仍然只是一个堆栈分配(aka自动)对象引用大小的插槽,仍然是null
  1. encarta = new Dictionary<Guid, int> { { Guid.NewGuid(), 10 } };
    • Subscribe内部,在机器语言级别,一个指向堆栈分配的encartalocal的指针(-ish)被保留并分配给该new Dictionary<Guid, int> { { Guid.NewGuid(), 10 } };
    • …这意味着encarta现在是而不是null
    • 执行返回到前一个函数。
  2. encartalocal现在是对GC堆上有效的字典对象的引用。但是从来没有调用过biggerDict[int].set_Item属性设置器来使biggerDict[0]成为encarta指向对象的非null引用。。请记住,除了真正的数组(T[]),所有其他具有索引器的类型都只是属性getter/setter方法的糖,这意味着对象引用是按值传递的,而不是按引用传递的——至少在没有ref返回属性的情况下是这样,Dictionary<K,V>没有这样做。

相关内容

  • 没有找到相关文章

最新更新