I have a dictionary inside a dictionary. I'd like to set a reference to the inner dictionary to a value after I'd added it to the outer dictionary as such:
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 } };
}
Unfortunately, this doesn't work and I'm not sure why ( Console.WriteLine(mammalSubscribers.First().Value); throws a null reference exception).
Can someone please explain why this doesn't work? In other words, why is mammalIdSubscribers[0] still null after the call to Subscribe with the ref keyword?
Your variables,
mammalIdSubscribersandmammalSubscribers, are very similarly named, so for the sake of clarity I'll renamemammalIdSubscribersto "outerDict" or maybe "biggerDict" whilemammalSubscribersis renamed to "encarta", because I used that as a reference a lot as a sprog.Line-by-line...
var biggerDict = new Dictionary<int, Dictionary<Guid, int>>();biggerDictdict.var mammalId = 0;biggerDict.TryGetValue(mammalId, out var encarta)false. Theoutparam is also an inlineoutdeclaration, and when you use inlineoutdeclarations withDictionary'sTryGetValuethen the new variable will benull(ordefault) when it returnsfalse.falsebecausebiggerDictis empty, as established earlier.encartaisnull.encartais a new GC reference-type variable on the stack, it is not an alias or "reference" to any part ofbiggerDict).TryGetValuecall is inside anif( !TryGetValue(...) )statement it means thatbiggerDict[mammalId] = encarta;will be evaluated.encartais stillnull.biggerDict[mammalId](akabiggerDict[0]) isnull.Subscribe(ref encarta);encartatoSubscribe.encartais not a reference to any slot or space withinbiggerDict: it's still just a stack-allocated (aka automatic) object-reference-sized slot that's stillnull.encarta = new Dictionary<Guid, int> { { Guid.NewGuid(), 10 } };Subscribe, at the machine-language level, a pointer(-ish) to the stack-allocatedencartalocal is deferenced and assigned to thatnew Dictionary<Guid, int> { { Guid.NewGuid(), 10 } };.encartais now notnull.encartalocal is now a reference to that valid dictionary object on the GC heap. But nothing ever invoked thebiggerDict[int].set_Itemproperty setter to makebiggerDict[0]a non-nullreference to the same object thatencartapoints to.T[]), all other types with indexers are just sugar over property getter/setter methods, which means object references are passed by value, and not references-passed-by-reference - at least not without aref-returning property, whichDictionary<K,V>does not do.