Search code examples
.netdictionaryc++-cli

How to modify values in a C++ CLI Dictionary


I try to modify a value of a struct that is stored as a value in a Dictionary<> but I can change the value but it is not updated in the dictionary itself:

using namespace System::Collections::Generic;

value struct amplPhse {
   double ampl;
   double phse;
   int    indx;
   bool   actv;
};

Dictionary<String ^, amplPhse> constituent;

bool
activate(String^ comp, bool flag) { 
    bool     retv = false;
    amplPhse ^trgt;

    if (constituent.ContainsKey(comp)) {
        trgt = constituent[comp]; 
        trgt->actv = flag; 
        retv = true;
    }
    return retv;
}

// Fill Dictionary with data ... and activate a component
fillDictionary();
activate("Comp", true);

After calling the activate() function, trgt->actv is set to true but the corresponding element in the constituent Dictionary is not. It is unclear to me why the value->actv flag is not accessed in the dictionary.


Solution

  • The problem is that your Dictionary elements are from a value struct which is a value type and not a reference type.
    You can see some more general info here: What is the difference between a reference type and value type in c#?.

    When you get a value from the Dictionary with this line:

    trgt = constituent[comp];
    

    You actually get a copy of the value stored in the Dictionary, and you modify this copy.

    You can solve it in 2 ways:

    1. If you can modify amplPhse to be a ref class the code will behave as you expect:
    ref class amplPhse {
    public:
        amplPhse(bool a) : actv{ a } {}
        bool   actv;
    };
    
    int main()
    {
        Dictionary<String^, amplPhse^> constituent;
        constituent.Add("k1", gcnew amplPhse{false});
        Console::WriteLine(constituent["k1"]->actv);
        amplPhse^ trgt = constituent["k1"];
        trgt->actv = true;
        Console::WriteLine(constituent["k1"]->actv);
        return 0;
    }
    
    1. If you cannot modify amplPhse, you can wrap it in a refrence type, and store the wrapper in the Dictionary:
    value struct amplPhse {
        bool   actv;
    };
    
    ref class amplPhseWrapper
    {
    public:
        amplPhseWrapper(amplPhse a) : ampl{ a } {}
        amplPhse ampl;
    };
    
    int main()
    {
        Dictionary<String^, amplPhseWrapper^> constituent;
        constituent.Add("k1", gcnew amplPhseWrapper{amplPhse{ false }});
        Console::WriteLine(constituent["k1"]->ampl.actv);
        amplPhseWrapper^ trgt = constituent["k1"];
        trgt->ampl.actv = true;
        Console::WriteLine(constituent["k1"]->ampl.actv);
        return 0;
    }
    

    In both cases the output is:

    False
    True
    

    I.e. the value in the Dictionary was modified.