How to Set Properties on Struct Instances Using Reflection

Is there a way to set properties on struct instances using reflection?

The value of rectangle is being boxed - but then you're losing the boxed value, which is what's being modified. Try this:

Rectangle rectangle = new Rectangle();
PropertyInfo propertyInfo = typeof(Rectangle).GetProperty("Height");
object boxed = rectangle;
propertyInfo.SetValue(boxed, 5, null);
rectangle = (Rectangle) boxed;

Use reflection to set the value of a field in a struct which is part of an array of structs

Get the FieldInfo for the array object (not the specific element).

If it's an array, cast it to a System.Array and use Array.SetValue to set the object's value.

C# Reflection - How to set field value for struct

Edit: I made this mistake again - fun fact; unbox-any returns the value; unbox returns the pointer to the data - which allows in-place mutate.

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 is mutating a boxed copy; you would need to unbox afterwards:

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

To do it in place, you would probably need the method to take a ref MyStruct, or to return a MyStruct. You could return the boxed copy, but that doesn't make it much easier to use. Frankly, it is moot: structs should not generally be mutable.

Why doesn't reflection set a property in a Struct?

It's because boxing your struct makes a copy of it, so you should box it earlier so you call the getter from the same data that you modified. The following code works:

    object _priceStruct = new PriceStruct(); //Box first
type = typeof(PriceStruct);
info = type.GetProperty("Value");
info.SetValue(_priceStruct, 32, null);
Console.WriteLine(((PriceStruct)_priceStruct).Value); //now unbox and get value

Debugger.Break();

c# Reflection of a nested struct property


If I have a property in an object that is a struct, whenever I use reflection to obtain the struct object and set any of it's properties/fields, the value on the object is not changed.

Well, no - because on this line:

object nestedStruct = pInfo.GetValue(myObj);

... you're copying the value, as it's a value type. You're also boxing it - which is fine, because it means when you mutate it in the intInfo.SetValue call, it's the boxed copy which is modified. (If you were boxing it at the point of calling intInfo.SetValue, that would really be hopeless.)

After you've made the change within the boxed copy, you then need to set it back into the property of the object:

pInfo.SetValue(myObject, nestedStruct);

It would be instructive to try doing what you're doing not using reflection:

MyObj myObj = new MyObj();
myObj.NestedStruct.MyIntProp = 23;

The compiler will give you an error on the last line, explaining that it's a silly thing to do:

Test.cs(20,9): error CS1612: Cannot modify the return value of 'MyObj.NestedStruct' because it is not a variable

Using Reflection to copy across equivalent properties between struct and class

The starting point for all this is the System.Type class. You can get an instance of this for your type using e.GetType().

To look for a field, use GetField. If that returns null, then the field doesn't exist at all.

If it returns a value (of type FieldInfo) then use GetValue to get the value and SetValue to set it.

Reflection is relatively slow, so if performance is a concern, grab the System.Type object ahead of time with something like System.Type.getType(name) and also get the FieldInfo objects. You don't need the actual instance of the class to do either of those two operations, though obviously you need it to get and set the field values.

Can I set the property of a struct using Expressions?

As noted in the comments, you really shouldn't create mutable structs. However, to answer the question, structs are value types and therefore a copy of your struct is passed to the Action and therefore the original person value is not changed.

You need a way to pass the struct by reference. However, expressions do not support creating "methods" that take parameters by reference.

What you can do is use the DynamicMethod class to do something like this:

public delegate void StructSetter<TInstance>(ref TInstance instance, object value) where TInstance : struct;

public StructSetter<TInstance> CreateSetter<TInstance>(
PropertyInfo propertyInfo,
bool includeNonPublic = false) where TInstance : struct
{
DynamicMethod method =
new DynamicMethod(
"Set",
typeof(void),
new [] { typeof(TInstance).MakeByRefType(), typeof(object )},
this.GetType());

var generator = method.GetILGenerator();

generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod(includeNonPublic));
generator.Emit(OpCodes.Ret);

return (StructSetter<TInstance>)method.CreateDelegate(typeof (StructSetter<TInstance> ));
}

We had to create a StructSetter delegate because the standard Action delegates do not support passing by reference.

Don't forget to cache the delegate or otherwise the cost of compiling is going to slow down your application.

Access fields of a Struct in an Object with Reflection

Frankly, there's no need whatsoever for TypedReference here - just a boxed struct should work fine:

    var amountField = obj.GetType().GetField("Amount");
object money = amountField.GetValue(obj);
var codeField = money.GetType().GetField("Code");
codeField.SetValue(money, "XXX");
amountField.SetValue(obj, money);

However! I will advise you of a few things:

  • public fields instead of properties are not usually a good idea; that will often bite you later
  • mutable structs (i.e. structs that can be changed after creation) are almost never a good idea, and will bite even more often, and bite harder
  • combining mutable structs and public fields compounds it, but making it very problematic to change later

Create instance of struct via reflection and set values

Your item is of type reflect.Value. You have to call Value.Interface() to obtain the value wrapped in it:

fmt.Println("reflected: \n" + JSONify(item.Interface()))

With this change, output will be (try it on the Go Playground):

normal: 
{
"Name": "Test",
"Age": 666
}
Name was set to reflectedNameValue
Age was set to 42
reflected:
{
"Name": "reflectedNameValue",
"Age": 42
}

reflect.Value itself is also a struct, but obviously trying to marshal it will not be identical to marshaling a Person struct value. reflect.Value does not implement marshaling the wrapped data to JSON.

C# Reflection SetValue on a struct exception

The first parameter for SetValue needs to be the instance whose property you want to set, but you've passed the property info instead. But this is hopeless anyway because MyStruct is a value type; SetValue will only take effect on the copy of the value you pass into it, not the original value. If you fix the SetValue parameter and change MyStruct to a class it will work as expected.



Related Topics



Leave a reply



Submit