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"
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"
A ref
parameter's reference can not leave the scope of the method that calls it. This is because the variable that is reference cannot be guaranteed to be in scope after the method call has finished. You need to use a tool other than ref
to create a layer of indirection allowing a variable a caller is using to be mutated.
Doing this is quite easy though. You simply need a class with a mutable member:
public class Pointer
{
public object Value { get; set; }
}
You can now write:
class Test1
{
IDictionary<string, Pointer> MyDict = new Dictionary<string, Pointer>();
public void saveVar(string key, Pointer pointer) //storing the ref to an int
{
MyDict.Add(key, pointer);
}
public void changeVar(string key) //changing any of them
{
if (MyDict[key].Value.GetType() == typeof(int))
{
MyDict[key].Value = (int)(MyDict[key].Value) * 2;
}
if (MyDict[key].Value.GetType() == typeof(string))
{
MyDict[key].Value = "Hello";
}
}
}
Since you're now mutating a reference type that the caller also has a reference to, they can observe the change to its value.
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
}
}
链接地址: http://www.djcxy.com/p/21162.html
上一篇: 如何在swift中比较Enum?
下一篇: 将参考存储在字典中