Luke De Feo
Luke De Feo

Reputation: 2165

How to set value of property where there is no setter

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?

Upvotes: 25

Views: 32233

Answers (6)

Sudin
Sudin

Reputation: 1

You can set property by reflection only when u have setter in your class

Upvotes: -1

Rubenisme
Rubenisme

Reputation: 816

Gleaning from the excellent answer above by @(Dan Solovay), we can now do something like this (made it easy to paste into LinqPad):

#nullable enable

void Main()
{
    var model = new MyModel();
    Console.WriteLine(model.Season);
    var setter = GetSetterForProperty<MyModel, SeasonEnum>(x => x.Season);
    setter?.Invoke(model, SeasonEnum.Summer);
    Console.WriteLine(model.Season);
}

enum SeasonEnum
{
    Unknown,
    Spring,
    Summer,
    Autumn,
    Winter
}

class MyModel
{
    public SeasonEnum Season { get; }
}

private const BindingFlags DeclaredOnlyLookup = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly;
private static Action<T, TValue>? GetSetterForProperty<T, TValue>(Expression<Func<T, TValue>> selector) where T : class
{
    var expression = selector.Body;
    var propertyInfo = expression.NodeType == ExpressionType.MemberAccess ? (PropertyInfo)((MemberExpression)expression).Member : null;

    if (propertyInfo is null)
    {
        return null;
    }

    var setter = GetPropertySetter(propertyInfo);

    return setter;

    static Action<T, TValue> GetPropertySetter(PropertyInfo prop)
    {
        var setter = prop.GetSetMethod(nonPublic: true);
        if (setter is not null)
        {
            return (obj, value) => setter.Invoke(obj, new object?[] { value });
        }

        var backingField = prop.DeclaringType?.GetField($"<{prop.Name}>k__BackingField", DeclaredOnlyLookup);
        if (backingField is 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);
    }
}

Upvotes: 2

Bruno Gozzi
Bruno Gozzi

Reputation: 147

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");

Upvotes: -2

Dan Solovay
Dan Solovay

Reputation: 3154

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";

Upvotes: 1

Abyte0
Abyte0

Reputation: 959

I do not suggest doing this on your application but for testing purpose it may be usefull...

Assuming you have:

public class MyClass
{
     public int MyNumber {get;}
}

You could do this if its for test purpose, I would not suggest to use this in your runtime code:

var field = typeof(MyClass).GetField("<MyNumber>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);
field.SetValue(anIstanceOfMyClass, 3);

Upvotes: 39

Kyle
Kyle

Reputation: 6684

You have to keep in mind that a property is just syntactic sugar for a pair of methods. One method (the getter) returns a value of the property type and one method (the setter) accepts a value of the property type.

There is no requirement that the getter and setter actually get or set anything. They're just methods, so they're allowed to do anything. The only requirement is that the getter return a value. From the outside there's no way you can really tell if there is a backing field. The getter could be getting computed every time it's called. It may be based on other properties.

So, no, there isn't really any way in general to "set" a property that doesn't have a setter.

Upvotes: 10

Related Questions