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"
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
}
}
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.
Related
This question already has answers here:
Nullable type as a generic parameter possible?
(13 answers)
Closed 1 year ago.
I am trying to adapt the following code, to also acceptable nullable type. Consider this:
public sealed class JsonDictionary
{
private readonly IDictionary<string, object> _data;
public static readonly Key<int> Foo = new Key<int> { Name = "FOO" };
public static readonly Key<double> Bar = new Key<double> { Name = "BAR" };
public static readonly Key<List<double>> Vec = new Key<List<double>> { Name = "VEC" };
public JsonDictionary()
{
_data = new Dictionary<string, object>();
}
public void Set<T>(Key<T> key, T obj)
{
_data[key.Name] = obj;
}
public T Get<T>(Key<T> key)
{
return (T)_data[key.Name];
}
public class Key<T>
{
public string Name { get; init; }
}
}
This works quite well:
var d = new JsonDictionary();
d.Set(JsonDictionary.Foo, 42);
d.Set(JsonDictionary.Bar, 3.14);
d.Set(JsonDictionary.Vec, new List<double> { 1.0, 2.0, 3.0 });
Assert.Equal(42, d.Get(JsonDictionary.Foo));
Assert.Equal(3.14, d.Get(JsonDictionary.Bar));
Assert.Equal(new List<double> { 1.0, 2.0, 3.0 }, d.Get(JsonDictionary.Vec));
How can I change the Set/Get API to also accept nullable type ? For instance:
d.Set(JsonDictionary.Foo, null);
d.Set(JsonDictionary.Bar, null);
d.Set(JsonDictionary.Vec, null);
The following naive attempt did not work for me:
public void Set<T>(Key<T> key, T? obj)
{
_data[key.Name] = obj;
}
Changing the base type as in:
public static readonly Key<int?> Foo = new Key<int?> { Name = "FOO" };
public static readonly Key<double?> Bar = new Key<double?> { Name = "BAR" };
public static readonly Key<List<double>?> Vec = new Key<List<double>?> { Name = "VEC" };
does work as expected, but requires a bit more typing ... any other solution directly at the Set function (with some constraints magic) ?
Another solution could be duplicating the code like this:
public void Set<T>(Key<T> key, T? obj) where T : struct
{
_data[key.Name] = obj;
}
public void Set<T>(Key<T> key, T? obj) where T : class
{
_data[key.Name] = obj;
}
which is also something I do not wish to do. Am I missing something fundamental in this strongly-typed structure ?
The Solution you stated of changing the base Data types to nullable types seems to be the only option. If you try to pass a null into a non-nullable data type such as int you will receive an error (which your compiler should warn you about) at runtime. Generics can only perform the functions of the data type it is passed!
// will receive an error here v
// passing a null into int
Set(new Key<int> { Name = "FOO" }, null);
public void Set(Key<int> key, int obj)
{
_data[key.Name] = obj;
}
this is essentially what your code was trying to do, the only way I can see it resolved is either by:
Fixing the Data type
Set<int?>(new Key<int?> { Name = "FOO" }, null);
public void Set<T>(Key<T> key, T obj)
{
// Has no issue as you are setting a null to a nullable datatype
_data[key.Name] = obj;
}
Discarding the null or reserving a value for null's
Set<T>(new Key<int> { Name = "FOO" }, null);
public void Set<T>(Key<T> key, object obj)
{
// check if obj is the same type as T if not sets the name to -1 in the key's type
// be care full though if you try to pass an int into a float you will also get -1
_data[key.Name] = (obj is T ? obj : (T)-1);
}
Good luck and sorry I couldn't help much
Let's say I have two class Foo and Bar as follows
public class Foo
{
private Bar _bar;
private string _whatever = "whatever";
public Foo()
{
_bar = new Bar();
}
public Bar TheBar
{
get
{
return _bar;
}
}
}
public class Bar
{
public string Name { get; set; }
}
I have an application that attaches to a process that is using these classes. I would like to see all instances of Foo type in .NET heap and inspect the TheBar.Name property or _whatever field of all
Foo instances present in .NET heap. I can find the type but I am not sure how to get the instance and see its properties. Any ideas how?
using (DataTarget target = DataTarget.AttachToProcess(processId, 30000))
{
string dacLocation = target.ClrVersions[0].TryGetDacLocation();
ClrRuntime runtime = target.CreateRuntime(dacLocation);
if (runtime != null)
{
ClrHeap heap = runtime.GetHeap();
foreach (ulong obj in heap.EnumerateObjects())
{
ClrType type = heap.GetObjectType(obj);
if (type.Name.Compare("Foo") == 0 )
{
// I would like to see value of TheBar.Name property or _whatever field of all instances of Foo type in the heap. How can I do it?
}
}
}
}
I don't think you can get property values directly because that would require you to run code and the target might not even be a process but a dump file.
You can definitely get an object's fields and their values. ClrType has a Fields property which you can use to loop through fields. Then you can call GetFieldValue for fields where HasSimpleValue is true.
A simple example:
private static void PrintFieldsForType(ClrRuntime runtime, string targetType)
{
ClrHeap heap = runtime.GetHeap();
foreach (var ptr in heap.EnumerateObjects())
{
ClrType type = heap.GetObjectType(ptr);
if (type.Name == targetType)
{
foreach(var field in type.Fields)
{
if (field.HasSimpleValue)
{
object value = field.GetFieldValue(ptr);
Console.WriteLine("{0} ({1}) = {2}", field.Name, field.Type.Name, value);
}
else
{
Console.WriteLine("{0} ({1})", field.Name, field.Type.Name);
}
}
}
}
}
So you could look for a field that has "Name", "_name", or something similar in it. If it is an auto-implemented property, the name will be something like <Name>k__BackingField.
Your scenario is a little more complicated in that you want to go into another object. To do that we can recursively inspect the fields. However note that in the general case you would want to keep track of which objects you've visited so you don't recurse indefinitely.
Here is an example that is more appropriate for this:
private static void PrintFieldsForType(ClrRuntime runtime, TextWriter writer, string targetType)
{
ClrHeap heap = runtime.GetHeap();
foreach (var ptr in heap.EnumerateObjects())
{
ClrType type = heap.GetObjectType(ptr);
if (type.Name == targetType)
{
writer.WriteLine("{0}:", targetType);
PrintFields(type, writer, ptr, 0);
}
}
}
private static void PrintFields(ClrType type, TextWriter writer, ulong ptr, int indentLevel)
{
string indent = new string(' ', indentLevel * 4);
foreach (var field in type.Fields)
{
writer.Write(indent);
if (field.IsObjectReference() && field.Type.Name != "System.String")
{
writer.WriteLine("{0} ({1})", field.Name, field.Type.Name);
ulong nextPtr = (ulong)field.GetFieldValue(ptr);
PrintFields(field.Type, writer, nextPtr, indentLevel + 1);
}
else if (field.HasSimpleValue)
{
object value = field.GetFieldValue(ptr);
writer.WriteLine("{0} ({1}) = {2}", field.Name, field.Type.Name, value);
}
else
{
writer.WriteLine("{0} ({1})", field.Name, field.Type.Name);
}
}
}
Here is how you could do it in LINQPad with ClrMD.Extensions:
var session = ClrMDSession.AttachToProcess(processId);
session.EnumerateClrObjects("*Foo").Dump(depth:3);
I don't know if it's possible to query the heap in such a way .
but an easy soluotion is to do something like this :
public class Foo
{
public static List<WeakReference<Foo>> allInstances = new List<WeakReference<Foo>>();
public Foo()
{
allInstances.Add(new WeakReference<Foo>(this));
}
}
Make sure to wrap then in a WeakReference so your collection won't keep them in the heap until the process ends .
I wanted to know: How to add new members to the list, so that when I change the values of variables will also change the list.
For example:
int a=4;
list<int> l=new list<int>();
l.Add(a);
a=5;
foreach(var v in l)
Console.WriteLine("a="+v);
Output:
a=4
thanks
You need to use reference types if you want that to happen.
With value types, such as int, you get a copy of the variable in the list, not a copy of the reference.
See Value Types and Reference Types on MSDN.
This will not work for a list of value type variables, each time you are changing a value type variable you get a new variable value copy in a stack. So a solution would be using some kind of reference type wrapper.
class NumericWrapper
{
public int Value { get; set; }
}
var items = new List<NumericWrapper>();
var item = new NumericWrapper { Value = 10 };
items.Add(item);
// should be 11 after this line of code
item.Value++;
You could build out a wrapper container and then just update the wrapper's value as needed. Something like below, for example:
//item class
public class Item<T>
{
T Value {get;set;}
}
//usage example
private List<String> items = new List<string>();
public void AddItem( Item<string> item)
{
items.Add(item);
}
public void SetItem(Item<T> item,string value)
{
item.Value=value;
}
You will have to wrap the int inside a reference type.
Try this:
internal class Program
{
private static void Main(string[] args)
{
IntWrapper a = 4;
var list = new List<IntWrapper>();
list.Add(a);
a.Value = 5;
//a = 5; //Dont do this. This will assign a new reference to a. Hence changes will not reflect inside list.
foreach (var v in list)
Console.WriteLine("a=" + v);
}
}
public class IntWrapper
{
public int Value;
public IntWrapper()
{
}
public IntWrapper(int value)
{
Value = value;
}
// User-defined conversion from IntWrapper to int
public static implicit operator int(IntWrapper d)
{
return d.Value;
}
// User-defined conversion from int to IntWrapper
public static implicit operator IntWrapper(int d)
{
return new IntWrapper(d);
}
public override string ToString()
{
return Value.ToString();
}
}
I'm writing my own method to convert an object graph to a custom object since the JavaScriptSerializer fires errors on null values.
So this is what I have so far:
internal static T ParseObjectGraph<T>(Dictionary<string, object> oGraph)
{
T generic = (T)Activator.CreateInstance<T>();
Type resType = typeof(T);
foreach (PropertyInfo pi in resType.GetProperties())
{
object outObj = new object();
if (oGraph.TryGetValue(pi.Name.ToLower(), out outObj))
{
Type outType = outObj.GetType();
if (outType == pi.PropertyType)
{
pi.SetValue(generic, outObj, null);
}
}
}
return generic;
}
Now the pi.SetValue() method runs, and doesn't fire an error but when I look at the properties of generic, it's still the same as it was before hand.
The first property it goes through is a boolean so the values end up like this
generic = an object of type MyCustomType
generic.property = false
outObj = true
pi = boolean property
outType = boolean
Then after the SetValue method runs, generic.property is still set to false.
PropertyInfo.SetValue/GetValue worked with struct with accurate using
struct Z
{
public int X { get; set; }
}
Z z1 = new Z();
z1.GetType().GetProperty("X").SetValue(z1, 100, null);
Console.WriteLine(z1.X); //z1.X dont changed
object z2 = new Z();
z2.GetType().GetProperty("X").SetValue(z2, 100, null);
Console.WriteLine(((Z)z2).X); //z2.x changed to 100
Z z3 = new Z();
object _z3 = z3;
_z3.GetType().GetProperty("X").SetValue(_z3, 100, null);
z3 = (Z)_z3;
Console.WriteLine(z3.X); //z3.x changed to 100
Correct way to change struct:
box struct
change property of boxed struct
assign boxed struct to source
Found the answer. Apparently, PropertyInfo.SetValue() and PropertyInfo.GetValue() do not work for structures, only classes.
MyCustomType was unfortunately a struct, so changing this to a class made it work.
The 3rd reply in this thread states why structures do not work and classes do.
EDIT: It does work with structs, see the marked answer.
So I took your method and made a unit test of it:
class PropertySetTest
{
static readonly Type resType = typeof(Car);
internal static T ParseObjectGraph<T>(Dictionary<string, object> oGraph)
{
T generic = (T)Activator.CreateInstance<T>();
foreach (PropertyInfo pi in resType.GetProperties())
{
//No need to new() this
object outObj; // = new object();
if (oGraph.TryGetValue(pi.Name.ToLower(), out outObj))
{
Type outType = outObj.GetType();
if (outType == pi.PropertyType)
{
pi.SetValue(generic, outObj, null);
}
}
}
return generic;
}
[Test]
public void Test()
{
var typeData = new Dictionary<String, Object> {{"color", "Blue"}};
var myCar = ParseObjectGraph<Car>(typeData);
Assert.AreEqual("Blue", myCar.Color);
}
}
internal class Car
{
public String Color { get; set; }
}
This passes. Can you make it not pass in the way that you are seeing?
EDIT: With your struct, it is only marginally more complicated. See Jon Skeet's answer here regarding what's going on. As for the working code:
class PropertySetTest
{
static readonly Type resType = typeof(Car);
internal static T ParseObjectGraph<T>(Dictionary<string, object> oGraph)
{
Object generic = Activator.CreateInstance<T>();
foreach (var pi in resType.GetProperties())
{
//No need to new() this
object outObj; // = new object();
if (oGraph.TryGetValue(pi.Name.ToLower(), out outObj))
{
var outType = outObj.GetType();
if (outType == pi.PropertyType)
pi.SetValue(generic, outObj, null);
}
}
return (T)generic;
}
[Test]
public void Test()
{
var typeData = new Dictionary<String, Object> {{"color", "Blue"}};
var myCar = ParseObjectGraph<Car>(typeData);
Assert.AreEqual("Blue", myCar.Color);
}
}
internal struct Car
{
public String Color { get; set; }
}
class Program
{
static void Main()
{
int i = 0;
whatever x = new whatever(i);
Console.WriteLine(x);
i = 1;
Console.WriteLine(x);
Console.ReadKey();
}
class whatever
{
public whatever(object variable)
{
this.variable = () => variable.ToString();
}
private Func<string> variable;
public string data;
public override string ToString()
{
data = variable();
return data;
}
}
Output:
0
0
what I want to do is get updated i's value.
If you want to capture the local variable then you've put the lambda in the wrong place. The lambda has to go where it can be closed over the outer variable you want to capture.
class Program
{
static void Main()
{
int i = 0;
var x = new Whatever<int>(()=>i);
Console.WriteLine(x);
i = 1;
Console.WriteLine(x);
Console.ReadKey();
}
}
class Whatever<T>
{
private Func<T> variable;
public Whatever(Func<T> func)
{
this.variable= func;
}
public override string ToString()
{
return this.variable().ToString();
}
}
Does that make sense? See, the lambda has to be where the "i" is declared, so that "i" is an outer variable of the lambda and therefore the lambda sees changes to it.
i is an integer (value type), which is passed by value - a copy of the value is passed to the whatever constructor. When you change its value on the Main method, it doesn't change what has been already passed to the class. So you can't get the updated value on whatever.
If you have an object which holds a field of an integer value, and then pass that object to whatever, then changes to that field will be reflected on the class.
Maybe the problem is that delegate is bound to boxed integer data. This is why you change your int and delegate evaluates to old boxed data.
Try it with constructor that takes an int.
But, yes it's true that ints are pased by value, so this will not work.
Pass delegate to ctor.
class Program
{
static void Main()
{
int i = 0;
whatever x = new whatever(() => i.ToString());
Console.WriteLine(x);
i = 1;
Console.WriteLine(x);
Console.ReadKey();
}
class whatever
{
public whatever(Func<string> someFunc)
{
this.variable = someFunc;
}
private Func<string> variable;
public string data;
public override string ToString()
{
data = variable();
return data;
}
}
}
Output:
0
1
Or as other have indicated:
class Program
{
static void Main()
{
var myRefType = new MyRefType();
myRefType.MyInt = 0;
var x = new whatever(myRefType);
Console.WriteLine(x);
myRefType.MyInt = 1;
Console.WriteLine(x);
Console.ReadKey();
}
class whatever
{
public whatever(MyRefType myRefType)
{
this.variable = () => myRefType.MyInt.ToString();
}
private Func<string> variable;
public override string ToString()
{
return variable();
}
}
class MyRefType
{
public int MyInt { get; set; }
}
}
Outputs:
0
1
Integers are of value type, not reference type.
int is value type, meaning its value is copied each time you use it and not its reference. The best way to make this work is make reference type around int:
class IntRef
{
public int Val;
}
You will need to always use IntRef.Val and passing the IntVal itself around will retain the reference.