22

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?

Community
  • 1
  • 1
Luke De Feo
  • 2,025
  • 3
  • 22
  • 40
  • 5
    Properties have no values. They may have setters which may alter the value of another field. If you're interested in finding the backing field of an auto-generated property, see this question: http://stackoverflow.com/questions/8817070/is-it-possible-to-access-backing-fields-behind-auto-implemented-properties – Rotem Dec 18 '13 at 18:14

5 Answers5

33

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);
Abyte0
  • 852
  • 8
  • 9
10

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.

Kyle
  • 6,500
  • 2
  • 31
  • 41
  • 1
    This is wrong. Using reflection, you can set the backing field. The problem is knowing what the backing field is. – Austin Salgat Sep 15 '18 at 19:39
  • 6
    @Salgat The point is that properties don't need to have backing fields, so you can't "know the backing field" in general, because it may not exist. For example, `public int Property { get { return 5; } }` is perfectly valid and does not have a backing field. Furthermore it's possible a property could depend on multiple backing fields, further muddying what it means to be able to "set" it. E.g. `public double Length { get { return Math.Sqrt(this.x * this.x + this.y * this.y); } }`. – Kyle Sep 15 '18 at 20:10
  • 1
    That's the whole point of reflection though. You can use something like the type.GetField method to determine if the backing field even exists (returning null if it doesn't). The OP clearly states, can you use reflection to set the value, which is usually possible. – Austin Salgat Sep 15 '18 at 20:18
  • 4
    @Salgat Kyle's answer clearly states that there is no *general* way of doing this and he is correct. – deloreyk Sep 15 '18 at 20:22
  • There is a general way of doing this that comes with an exception (when the property is not auto-implemented). But at this point, we're arguing semantics. – Austin Salgat Sep 16 '18 at 18:55
  • Downvoting. This is factually incorrect and should be updated or removed. – Dan Solovay Mar 20 '21 at 17:28
  • @DanSolovay What's incorrect about it? Could you elaborate? – Shelby Oldfield Oct 07 '22 at 19:56
  • @ShelbyOldfield Reflection can be used to set a property, so it's not correct to say "there isn't really any way ... to "set" a property that doesn't have a setter." I cite a library that makes use of this admittedly obscure capability. (I almost never downvote, but I figure something that is factually incorrect requires that, even if the reason is a bit obscure.. I hope that seems reasonable.) See my answer below for more detail: https://stackoverflow.com/a/66724678/402949 – Dan Solovay Oct 15 '22 at 17:31
  • 1
    @DanSolovay You may have missed the point. While that library does work for auto-properties, I'm not sure those backing field names are guaranteed, and regardless, that only works for *auto-properties*, not properties in general. This answer is explicitly talking about properties in general, and it's correct to say that there's no general way to call the setter on a property which has no setter. – Shelby Oldfield Oct 16 '22 at 19:42
1

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

Dan Solovay
  • 3,134
  • 3
  • 26
  • 55
1

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);
    }
}
Rubenisme
  • 787
  • 1
  • 8
  • 15
-1

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");
Bruno Gozzi
  • 139
  • 1
  • 4