56

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.

Victor Chelaru
  • 4,491
  • 3
  • 35
  • 49

2 Answers2

86

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;
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 9
    Incidentally, this is a good example of one of the perils of mutable value types. – Dan Bryant Jun 08 '11 at 14:54
  • 2
    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. :-) – user541686 Jun 08 '11 at 15:31
  • 1
    +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. – Tim Long Nov 18 '13 at 03:42
  • 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) – Giuseppe Oct 13 '14 at 13:54
  • 2
    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) – Giuseppe Oct 13 '14 at 13:54
  • 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? – Sнаđошƒаӽ May 13 '18 at 03:05
  • 1
    @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. – Jon Skeet May 13 '18 at 08:14
  • @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. – Sнаđошƒаӽ May 13 '18 at 09:57
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.

user541686
  • 205,094
  • 128
  • 528
  • 886
  • 1
    Note that *"This API is not CLS-compliant."*. – talles Aug 11 '15 at 14:31
  • `__makeref()` not implement in Unity3d with IL2CPP – Abedron May 24 '18 at 06:49
  • 1
    Unfortunately, apart from not being CLS-compliant, [`SetValueDirect`](https://msdn.microsoft.com/en-us/library/system.reflection.fieldinfo.setvaluedirect(v=vs.110).aspx) and [`TypedReference`](https://msdn.microsoft.com/en-us/library/system.typedreference(v=vs.110).aspx) simply do not work with `readonly` fields in structs. [`SetValue`](https://msdn.microsoft.com/en-us/library/system.reflection.fieldinfo.setvalue(v=vs.110).aspx) does work in those conditions. – Eduard Dumitru Jun 21 '18 at 11:10
  • @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. – Aly Elhaddad May 04 '19 at 16:29
  • What if the issue you are using Reflection to get the original object e.g. you can't call `__makeref` on `GetValue`... – NetMage Jan 24 '22 at 23:12