0

Suppose I have a class and an interface that looks like this:

[Serializable]
public class MyClass : IClass
{
   public int Prop => 42;
}

public interface IClass
{
   int Prop { get; }
}

As you can see there's no Setter. But I was hoping to use reflection to change the Prop property but I can't seem to do it. Here's what I have so far:

var property = typeof(MyClass).GetProperty("Prop", BindingFlags.Instance | BindingFlags.Public);
property.SetValue(instaneOfClass, 31);

I keep getting this error: System.ArgumentException: Property set method not found.

Shouldn't this work? How can I set the property without using a setter?

idude
  • 4,654
  • 8
  • 35
  • 49
  • 1
    I closed it as duplicate of earlier suggested answer as it already contained text "complex code". I've also edited the answer to add "complex code includes `int TheAnswer => 42;` so it clearly answer this one too. – Alexei Levenkov Jul 12 '21 at 23:00
  • The mystery that remains is: Where is that int of value '42' stored? – lidqy Jul 13 '21 at 10:57

2 Answers2

4

Auto implemented properties have backing fields named <PropName>k__BackingField.

In your case you would access this backing field like this:

var property = typeof(MyClass).GetField("<Prop>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);
property.SetValue(instanceOfClass, 31);

Note that I'm calling GetField() instead of GetProperty(), and using BindingFlags.NonPublic instead of BindingFlags.Public, since the backing field is private.

However, since this is an expression bodied member (lambda syntax), no backing field is generated. Instead of relying on another member, the getter simply returns the result of the given expression, and the compiler doesn't need to generate a backing field.

Implementing your property with an explicit getter will generate a backing field, and the above code will work.

public class MyClass : IClass
{
   public int Prop { get; } = 42;
}
lrpe
  • 730
  • 1
  • 5
  • 13
2

If you specify a getter you'll have a compiler generated field that contains the property name. You can then modify the value of this field using reflection (GetFields, SetValue):

public class Foo        {
        public Foo() => Bar = 42;
        public int Bar { get; }        

        static void Main()  {
           var f = new Foo();
           Console.WriteLine(f.Bar); // ==> 42;
           f.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic).First(x => x.Name.Contains("Bar")).SetValue(f, 43);
           Console.WriteLine(f.Bar); // ==> 43;
  }
}

It does not work for a computed property as in your example.

lidqy
  • 1,891
  • 1
  • 9
  • 11