C # Reflection - How to set field value for struct

How to set value in struct field - myStruct.myField with reflection using DynamicMethod? When I call setter(myStruct, 111) value was not set, because MyStruct is the type of value. Console.WriteLine(myStruct.myField) shows a value of 3.
How to change the GetDelegate method to set the value in myStruct.myField ?

 public struct MyStruct { public int myField; } public delegate void SetHandler(object source, object value); private static SetHandler GetDelegate(Type type, FieldInfo fieldInfo) { DynamicMethod dm = new DynamicMethod("setter", typeof(void), new Type[] { typeof(object), typeof(object) }, type, true); ILGenerator setGenerator = dm.GetILGenerator(); setGenerator.Emit(OpCodes.Ldarg_0); setGenerator.DeclareLocal(type); setGenerator.Emit(OpCodes.Unbox_Any, type); setGenerator.Emit(OpCodes.Stloc_0); setGenerator.Emit(OpCodes.Ldloca_S, 0); setGenerator.Emit(OpCodes.Ldarg_1); setGenerator.Emit(OpCodes.Unbox_Any, fieldInfo.FieldType); setGenerator.Emit(OpCodes.Stfld, fieldInfo); setGenerator.Emit(OpCodes.Ldloc, 0); setGenerator.Emit(OpCodes.Box, type); setGenerator.Emit(OpCodes.Ret); return (SetHandler)dm.CreateDelegate(typeof(SetHandler)); } MyStruct myStruct = new MyStruct(); myStruct.myField = 3; FieldInfo fi = typeof(MyStruct).GetField("myField", BindingFlags.Public | BindingFlags.Instance); SetHandler setter = GetDelegate(typeof(MyStruct), fi); setter(myStruct, 111); Console.WriteLine(myStruct.myField); 
+5
source share
5 answers

Edit: I made this mistake again - a funny fact; unbox-any returns a value; unbox returns a pointer to data - which allows mutation in place.

Here's the working IL generation:

  setGenerator.Emit(OpCodes.Ldarg_0); setGenerator.Emit(OpCodes.Unbox, type); setGenerator.Emit(OpCodes.Ldarg_1); setGenerator.Emit(OpCodes.Unbox_Any, fieldInfo.FieldType); setGenerator.Emit(OpCodes.Stfld, fieldInfo); setGenerator.Emit(OpCodes.Ret); 

But! This mutates a boxed copy; you will need to unzip later:

  object obj = myStruct; setter(obj, 111); MyStruct andBackAgain = (MyStruct)obj; Console.WriteLine(andBackAgain.myField); Console.WriteLine(myStruct.myField); 

To do this in place, you probably need a method to accept ref MyStruct or to return MyStruct . You can return the copy in the box, but this does not make it much easier to use. Frankly, this is a moot point :. Structures usually should not be mutable

+10
source

I think Servy points out in the comments that it is probably better not to have a mutable structure and instead create a copy of the structure with a new value. You can use the SetValueDirect method for an instance of FieldInfo if you really want to use reflection to mutate the structure, but use the undocumented __makeref method to get TypedReference .

I really do not recommend using this code; this is pure to show that this is possible:

 MyStruct myStruct = new MyStruct(); myStruct.myField = 3; FieldInfo fi = typeof(MyStruct).GetField("myField", BindingFlags.Public | BindingFlags.Instance); TypedReference reference = __makeref(myStruct); fi.SetValueDirect(reference, 111); Console.WriteLine(myStruct.myField); //prints 111 
+6
source

Here is the code with ref:

 public delegate void SetHandler<T>(ref T source, object value) where T : struct; private static SetHandler<T> GetDelegate<T>(FieldInfo fieldInfo) where T : struct { var type = typeof(T); DynamicMethod dm = new DynamicMethod("setter", typeof(void), new Type[] { type.MakeByRefType(), typeof(object) }, type, true); ILGenerator setGenerator = dm.GetILGenerator(); setGenerator.Emit(OpCodes.Ldarg_0); setGenerator.DeclareLocal(type); setGenerator.Emit(OpCodes.Ldarg_0); setGenerator.Emit(OpCodes.Ldnull); setGenerator.Emit(OpCodes.Stind_Ref); setGenerator.Emit(OpCodes.Ldarg_1); setGenerator.Emit(OpCodes.Unbox_Any, fieldInfo.FieldType); setGenerator.Emit(OpCodes.Stfld, fieldInfo); setGenerator.Emit(OpCodes.Ldloc, 0); setGenerator.Emit(OpCodes.Box, type); setGenerator.Emit(OpCodes.Ret); return (SetHandler<T>)dm.CreateDelegate(typeof(SetHandler<>).MakeGenericType(type)); } static void Main(string[] args) { MyStruct myStruct = new MyStruct(); myStruct.myField = 3; FieldInfo fi = typeof(MyStruct).GetField("myField", BindingFlags.Public | BindingFlags.Instance); var setter = GetDelegate<MyStruct>(fi); setter(ref myStruct, 111); Console.WriteLine(myStruct.myField); } 
+1
source

To add to the other answers, you really can insert inside the delegate if your method also returns a modified structure.

Since my IL-fu is not so big, so you do it with a simple reflection:

 // the good side is that you can use generic delegates for added type safety public delegate T SetHandler<T>(T source, object value); private static SetHandler<T> GetDelegate<T>(FieldInfo fieldInfo) { return (s, val) => { object obj = s; // we have to box before calling SetValue fieldInfo.SetValue(obj, val); return (T)obj; }; } 

This means that you will need to get the return value as follows:

 SetHandler<MyStruct> setter = GetDelegate<MyStruct>(fi); myStruct = setter(myStruct, 111); Console.WriteLine(myStruct.myField); 

But there is no need to insert it before calling setter .

Alternatively, you can pass the structure using the ref keyword, which will result in:

 public delegate void SetHandler<T>(ref T source, object value); private static SetHandler<T> GetDelegate<T>(FieldInfo fieldInfo) { return (ref T s, object val) => { object obj = s; fieldInfo.SetValue(obj, val); s = (T)obj; }; } SetHandler<MyStruct> setter = GetDelegate<MyStruct>(fi); setter(ref myStruct, 111); // no need to return anymore Console.WriteLine(myStruct.myField); 
0
source

The easiest way to use Reflection to set the fields or properties of a structure is to bind the structure, transfer the structure in the box to SetField or SetProperty , and then unpack the structure after all the desired manipulations have been completed.

 public static class refStructTest { struct S1 { public int x; public int y { get; set; } public override string ToString() { return String.Format("[{0},{1}]", x, y); } } public static void test() { var s = default(S1); sx = 2; sy = 3; Object obj = s; var fld = typeof(S1).GetField("x"); var prop = typeof(S1).GetProperty("y"); fld.SetValue(obj,5); prop.SetValue(obj,6,null); s = (S1)obj; Console.WriteLine("Result={0}", s); } } 

According to the ECMA documentation, each value type is associated with two types of things: storage location type and heap type type. The type of the heap object, like all types of heap objects, will behave using referential semantics; passing a reference to a heap object to a method such as SetValue thus changes the object to which the link was passed.

Note for VB users: VB.NET has really annoying behavior that almost makes sense in the Option Strict On dialect, but that even exists in the Option Strict Off dialect: if a variable of compile-time type Object that contains a box structure reference is assigned to another variable of the same type or passed as a parameter of type Object , VB.NET will store or pass a link to a copy of the source object. When writing code like the one above in VB.NET, you should make obj type of ValueType , not Object , to prevent this behavior.

0
source

Source: https://habr.com/ru/post/1201666/


All Articles