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

2

58

I'm trying to write some code that sets a property on a struct (important that it's a property on a struct) and it's failing:

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

The Height value (as reported by the debugger) never gets set to anything - it stays at the default value of 0.

I have done plenty of reflection on classes before and this has worked fine. Also, I know that when dealing with structs, you need to use FieldInfo.SetValueDirect if setting a field, but I don't know of an equivalent for PropertyInfo.

Expanse answered 8/6, 2011 at 14:35 Comment(0)
R
92

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;
Rattan answered 8/6, 2011 at 14:38 Comment(8)
Incidentally, this is a good example of one of the perils of mutable value types.Sandisandidge
Just make sure that you're not doing this in a loop (or that performance isn't an issue), especially if the structure is large. :-)Anticlastic
+1 I had exactly the same issue. @Dan Bryant - while I agree that mutable structs are evil, the reason I am doing this is to make a factory class for immutable structs and I need to be able to set readonly properties. I'm doing something sort of similar to what the MVC Model Binder subsystem does, although much simpler and nothing to do with the web. So I'm making immutable structs, but I'm allowing myself to mutate them once only, at the point of creation, so I think that will avoid all the potential evil.Unexceptional
Please, note that VB.net has a different behaviour and the following code (which seems apparently equivalent) will not work: Dim _rectangle As New Rectangle() Dim _propertyInfo As PropertyInfo = GetType(Rectangle).GetProperty("Height") Dim boxed As Object = _rectangle _propertyInfo.SetValue(boxed, 5, Nothing) _rectangle = DirectCast(boxed, Rectangle)Dogcart
Who's using VB.net should instead use the following example: Dim _rectangle As New Rectangle() Dim _propertyInfo As PropertyInfo = GetType(Rectangle).GetProperty("Height") Dim boxed As ValueType = _rectangle _propertyInfo.SetValue(boxed, 5, Nothing) _rectangle = DirectCast(boxed, Rectangle)Dogcart
Can you please explain why boxing is needed for using reflection on structs? I know reflection APIs take Object, but isn't struct an object too?Jermainejerman
@Sнаđошƒаӽ: No, a value type value isn't an object, despite the inheritance hierarchy. Boxing is precisely the process of allocating an object to hold a value type value, and then getting a reference to that object. There isn't space here to go into the difference between value types and reference types, but I strongly urge you to research them.Rattan
@JonSkeet Much appreciated. a value type value isn't an object, despite the inheritance hierarchy - as you pointed out, I definitely need to learn a lot on that. Thanks.Jermainejerman
A
16

Ever heard of SetValueDirect? There's a reason they made it. :)

struct MyStruct { public int Field; }

static class Program
{
    static void Main()
    {
        var s = new MyStruct();
        s.GetType().GetField("Field").SetValueDirect(__makeref(s), 5);
        System.Console.WriteLine(s.Field); //Prints 5
    }
}

There's other methods than the undocumented __makeref which you could use (see System.TypedReference) but they're more painful.

Anticlastic answered 8/6, 2011 at 14:59 Comment(5)
Note that "This API is not CLS-compliant.".Infarction
__makeref() not implement in Unity3d with IL2CPPLignocellulose
Unfortunately, apart from not being CLS-compliant, SetValueDirect and TypedReference simply do not work with readonly fields in structs. SetValue does work in those conditions.Linis
@EduardDumitru I'm not sure whether or not SetValueDirect's implementation has changed ever since you wrote your comment; however, I've just used it to modify a public readonly field on a struct, and it worked.Nonbeliever
What if the issue you are using Reflection to get the original object e.g. you can't call __makeref on GetValue...Airing

© 2022 - 2024 — McMap. All rights reserved.