Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Store reference to an object in dictionary

I have been searching for a way to save the references of variables of various types into a dictionary, together with a corresponding key. Then i would like to modify the instance of the variable by accessing its reference through the dictionary by its key. For storing the references, i tried to use <object>, but without success. Neither Dictionaries nor Lists accept anything like Dictionary<string, ref int>. The following code compiles, but seems to update the variables by value only. Any ideas or workarounds?

Here's the (tested) code:

class Test1
{
    IDictionary<string, object> MyDict = new Dictionary<string, object>();

    public void saveVar(string key, ref int v) //storing the ref to an int
    {
        MyDict.Add(key, v);
    }
    public void saveVar(string key, ref string s) //storing the ref to a string
    {
        MyDict.Add(key, s);
    }

    public void changeVar(string key) //changing any of them
    {
        if(MyDict.GetType() == typeof(int))
        {
            MyDict[key] = (int)MyDict[key] * 2;
        }
        if(MyDict.GetType() == typeof(string))
        {
            MyDict[key] = "Hello";
        }
    }
}

And this is how i call the methods of the class

Test1 t1 = new Test1();
int myInt = 3;
string myString = "defaultString";

Console.WriteLine(myInt); //returns "3"
Console.WriteLine(myString); //returns "defaultString"

t1.saveVar("key1", ref myInt);
t1.saveVar("key2", ref myString);

t1.changeVar("key1");
t1.changeVar("key2");

Console.WriteLine(myInt); //should return "6"
Console.WriteLine(myString); //should return "Hello"
like image 550
Thomas.Hut Avatar asked Jun 20 '14 13:06

Thomas.Hut


People also ask

Can we store object in dictionary C#?

In C# object is one of the root and parent class for all the subclasses; based on the requirement, we can able to convert the object to other types like a dictionary, strings like that dictionary is one of the generic collection class that can be used for to store the datas as key/value pairs by using the System.

Can dictionary store different data types?

One can only put one type of object into a dictionary. If one wants to put a variety of types of data into the same dictionary, e.g. for configuration information or other common data stores, the superclass of all possible held data types must be used to define the dictionary.

Are Dictionaries reference types?

Examples of reference books include: almanacs, atlases, dictionaries, directories, encyclopedias and indexes.

What is a Dictionary object?

A Dictionary object is the equivalent of a PERL associative array. Items, which can be any form of data, are stored in the array. Each item is associated with a unique key. The key is used to retrieve an individual item and is usually an integer or a string, but can be anything except an array.


2 Answers

Apart from the problem Kevin points out, you need to wrap your value types in some kind of reference type.

The problem, as you've figured out, is that generic types don't work with the ref keyword, and when you assign a new value type into your dictionary, it's replacing the reference with a different reference, not updating it. There is no way to retain the ref semantics once you assign it to the dictionary.

But, what you could do is something like this, simply wrap the value type in a reference type:

public class MyRef<T> {
    public T Ref {get;set;}
}

public class Test1
{
    Dictionary<string, object> MyDict = new Dictionary<string, object>();

    public void saveVar(string key, object v) 
    {
        MyDict.Add(key, v);
    }

    public void changeVar(string key, object newValue) //changing any of them
    {
        var ref1 = MyDict[key] as MyRef<int>;
        if (ref1 != null) {
            ref1.Ref = (int)newValue;
            return; // no sense in wasting cpu cycles
        }

        var ref2 = MyDict[key] as MyRef<string>;
        if (ref2 != null) {
            ref2.Ref = newValue.ToString();
        }
    }

    public void DoIt()
    {
        var v = new MyRef<int> { Ref = 1 };

        saveVar("First", v);
        changeVar("First", 2);

        Console.WriteLine(v.Ref.ToString()); // Should print 2
        Console.WriteLine(((MyRef<int>)MyDict["First"]).Ref.ToString()); // should also print 2
    }
}
like image 65
Erik Funkenbusch Avatar answered Oct 02 '22 01:10

Erik Funkenbusch


The best solution I can think of for this is to store delegates in the dictionary that will allow you to retrieve and modify the variables.

Let’s start by declaring a type that contains a getter and a setter delegate:

sealed class VariableReference
{
    public Func<object> Get { get; private set; }
    public Action<object> Set { get; private set; }
    public VariableReference(Func<object> getter, Action<object> setter)
    {
        Get = getter;
        Set = setter;
    }
}

The dictionary would have the type:

Dictionary<string, VariableReference>

To store a variable, say foo of type string, in the dictionary, you’d write the following:

myDic.Add(key, new VariableReference(
    () => foo,                      // getter
    val => { foo = (string) val; }  // setter
));

To retrieve the value of a variable, you’d write

var value = myDic[key].Get();

To change the value of a variable to newValue, you’d write

myDic[key].Set(newValue);

This way, the variable that you’re changing is genuinely the original variable foo, and foo can be anything (a local variable, a parameter, a field on an object, a static field... even a property).

Putting this all together, this is what the class Test1 would look like:

class Test1
{
    Dictionary<string, VariableReference> MyDict = new Dictionary<string, VariableReference>();

    public void saveVar(string key, Func<object> getter, Action<object> setter)
    {
        MyDict.Add(key, new VariableReference(getter, setter));
    }

    public void changeVar(string key) // changing any of them
    {
        if (MyDict[key].Get() is int)
        {
            MyDict[key].Set((int)MyDict[key].Get() * 2);
        }
        else if (MyDict[key].Get() is string)
        {
            MyDict[key].Set("Hello");
        }
    }
}

// ...

Test1 t1 = new Test1();
int myInt = 3;
string myString = "defaultString";

Console.WriteLine(myInt);    // prints "3"
Console.WriteLine(myString); // prints "defaultString"

t1.saveVar("key1", () => myInt, v => { myInt = (int) v; });
t1.saveVar("key2", () => myString, v => { myString = (string) v; });

t1.changeVar("key1");
t1.changeVar("key2");

Console.WriteLine(myInt);    // actually prints "6"
Console.WriteLine(myString); // actually prints "Hello"
like image 42
Timwi Avatar answered Oct 01 '22 23:10

Timwi