Experienced an unexpected behavior when using Dictionary type and passing by reference. Within a nested call, the reference to the underlaying base dictionary seems to be lost or replaced. All child method calls are passing the dictionary by ref.
public void Main() {
// Get Dictionary and fill if empty - Type 1 Dictionary
Dictionary<string, string> dictStored = DictionaryFetch(1);
// Pull from previously filled and stored static dictionary
dictStored = DictionaryFetch(1);
}
My understanding is that I am passing the address reference of the local type (which itself is a reference type object - Dictionary).
If a dictionary is assigned on the child method that action occurs on the parent dictionary (meaning its the same object, same memory address ref).
Within DictionaryFetch()
, if the dictionary is empty and needs to be created, the final switch..case assignment to the static dictionary should not be required. I would like to remove this final reassignment to the root static dictionary.
// Method to find and return the correct Dictionary by Type.
void DictionaryFetch(int DictType)
{
Dictionary<string, string> localDict = new();
// Retrieve Dict pass by reference
// localDict will contain static dictA or dictB contents
DictionaryFetchStatic(dictType, ref localDict);
// Check dictionary, create if empty
if (localDict.Count == 0)
{
// Method to populate localDict with entries.
CreateDictionary(ref localDict);
// Here is the gotcha, the static dictA or dictB is still empty,
// but localDict is populated - hence the need for the following switch statement
switch(dictType)
{
case 1:
dictA = localDict;
break;
case 2:
dictB = localDict;
break;
};
}
return localDict;
}
What am I missing? Why is the dictA not populated before the final switch..case statement in DictionaryFetch()
?
static Dictionary<string, string> dictA = new();
static Dictionary<string, string> dictB = new();
void DictionaryFetchStatic(int dictType, ref Dictionary<string, string> dictRequester)
{
switch(dictType)
{
case 1:
dictRequester = dictA;
break;
case 2:
dictRequester = dictB;
break;
};
}
void CreateDictionary(ref Dictionary<string, string> dictRequester)
{
// Just an example of creating a dictionary using DbContext, its generating a new Dict in response.
dictRequester = _context.owner.Where(x => x.type == 'abc')
.ToDictionary(o => o.key, o => o.value);
}
Thanks for helpful response tips. In my real-world scenario the fill method will use a DbContext and will generate a new very large dictionary.
The solution is to avoid use of local scope defined dictionary var, instead define a ref dictionary within DictControllerMethod(). This avoids the [pointer to a pointer] issue as reported in my question - just using and passing a single pointer to the root dictionary.
Its use of local defined dictionary vars that cause the unnecessary additional layers. The use of a reference-type[dictionary] by value added to the confusion.
public void Main() {
int dictionaryType = 1;
// Get Dictionary and fill if empty - Type 1 Dictionary
Dictionary<string, string> dictStored = DictionaryFetch(dictionaryType);
// Pull from previously filled and stored static dictionary
dictStored = DictionaryFetch(dictionaryType);
}
Methods:
static Dictionary<string, string> dictA = new();
static Dictionary<string, string> dictB = new();
ref Dictionary<string, string> DictionaryFetchStatic(int dictionaryType)
{
switch(dictionaryType)
{
case 1:
return dictA;
case 2:
return dictB;
};
}
// Method to find and return the correct Dictionary by Type.
Dictionary<string, string> DictionaryFetch(int dictionaryType)
{
// localDict will point to static dictA or dictB Dictionary reference type.
ref Dictionary<string, string> localDict = ref DictionaryFetchStatic(dictionaryType);
// Check dict, create if empty
if (localDict.Count == 0)
{
// Method to fill localDict with entries.
CreateDictionary(ref localDict);
}
return localDict;
}
void CreateDictionary(ref Dictionary<string, string> dictRequester)
{
// Just an example of filling a dictionary using DbContext, its generating a new Dict in response.
dictRequester = _context.owner.Where(x => x.type == 'abc')
.ToDictionary(o => o.key, o => o.value);
}