How to set value of property where there is no setter

Viewed 23535

I have seen various questions raised and answered where we can invoke a private setter using reflection such as this one:

Is it possible to get a property's private setter through reflection?

However I have some code which has a property i need to set but cant because there is no setter, I cant add a setter as this isn't my code. Is there a way to somehow set the value using reflection in this scenario?

4 Answers

Adding a practical use case to @abyte0's answer.

Some libraries make use of reflection to set properties this way. For example, see this sample code from https://github.com/natemcmaster/CommandLineUtils:

using System;
using McMaster.Extensions.CommandLineUtils;

public class Program
{
    public static int Main(string[] args)
        => CommandLineApplication.Execute<Program>(args);

    [Option(Description = "The subject")]
    public string Subject { get; } = "world";

    [Option(ShortName = "n")]
    public int Count { get; } = 1;

    private void OnExecute()
    {
        for (var i = 0; i < Count; i++)
        {
            Console.WriteLine($"Hello {Subject}!");
        }
    }
}

Behind the scenes, this syntax is implemented with this code:

        public static SetPropertyDelegate GetPropertySetter(PropertyInfo prop)
        {
            var setter = prop.GetSetMethod(nonPublic: true);
            if (setter != null)
            {
                return (obj, value) => setter.Invoke(obj, new object?[] { value });
            }
            else
            {
                var backingField = prop.DeclaringType.GetField($"<{prop.Name}>k__BackingField", DeclaredOnlyLookup);
                if (backingField == null)
                {
                    throw new InvalidOperationException(
                        $"Could not find a way to set {prop.DeclaringType.FullName}.{prop.Name}. Try adding a private setter.");
                }

                return (obj, value) => backingField.SetValue(obj, value);
            }
        }

The practical value here is having the code express that the only way a value should be set is through a command line invocation. This is allowed: hello.exe -s world but this is not: Subject = "some other value";

Could you use "propertyInfo" approach. See the example below:

With a class like this:

public class MyClass {
    public string MyAttribute{ get; } // --> blocked attribute

}

Use this code to change property value:

var instanceOfMyClass = new MyClass();

typeof(MyClass).GetProperty("MyAttribute")?.SetValue(instanceOfMyClass , "SomeValue");

or maybe you can write it a little more "elegant" using nameof.

typeof(MyClass).GetProperty(nameof(MyClass.MyAttribute))?.SetValue(instanceOfMyClass , "SomeValue");
Related