Is it possible to set private property via reflection?
Asked Answered
W

6

154

Can I set a private property via reflection?

public abstract class Entity
{
    private int _id;
    private DateTime? _createdOn;
    public virtual T Id
    {
        get { return _id; }
        private set { ChangePropertyAndNotify(ref _id, value, x => Id); }
    }
    public virtual DateTime? CreatedOn
    {
        get { return _createdOn; }
        private set { ChangePropertyAndNotify(ref _createdOn, value, x => CreatedOn); }
    }
}

I've tried the following and it does not work, where t represents a type of Entity:

var t = typeof(Entity);
var mi = t.GetMethod("set_CreatedOn", BindingFlags.Instance | BindingFlags.NonPublic);

I guess I can do this but I can't work it out.

Walrus answered 14/10, 2009 at 11:37 Comment(4)
I know this is late, but I found a need for this thought I would share my 'why'. I needed to overcome an inconvenience in some third-party software. Specifically, I was using the Crystal Reports ExportToStream method. The way this method was written, access to the stream's internal buffer was not allowed. In order to send the report to the browser, I had to copy the stream into a new buffer (100K+), then send it out. By setting the private '_exposable' field in the stream object to 'true', I was able to send the internal buffer out directly, saving a 100K+ allocation on each request.Favorite
Why? Let's say you have private setters on your Id properties on all your domain object and you want to implement repository tests. Then only in your repository test project you'll want to be able to set the Id property.Ramekin
Another use scenario: setting auto-generated fields like "creation date" when importing data.Sisterly
Another why is I'm just curious if it's possible. That's how I ended up viewing this question.Flagwaving
K
120
t.GetProperty("CreatedOn")
    .SetValue(obj, new DateTime(2009, 10, 14), null);

EDIT: Since the property itself is public, you apparently don't need to use BindingFlags.NonPublic to find it. Calling SetValue despite the the setter having less accessibility still does what you expect.

Kroon answered 14/10, 2009 at 11:42 Comment(4)
To be fair, it depends on the trust level, but the answer seems valid.Nitaniter
Property set method not found at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)Weeden
This works fine for me if I am not using a virtual property. If I SetValue with a virtual property, this does not seem to work.Joann
I have a doubt here. If the class is an abstract type. How can we create an object of it and pass it as the first parameter of SetValue?Rub
C
140

Yes, it is:

/// <summary>
/// Returns a _private_ Property Value from a given Object. Uses Reflection.
/// Throws a ArgumentOutOfRangeException if the Property is not found.
/// </summary>
/// <typeparam name="T">Type of the Property</typeparam>
/// <param name="obj">Object from where the Property Value is returned</param>
/// <param name="propName">Propertyname as string.</param>
/// <returns>PropertyValue</returns>
public static T GetPrivatePropertyValue<T>(this object obj, string propName)
{
    if (obj == null) throw new ArgumentNullException("obj");
    PropertyInfo pi = obj.GetType().GetProperty(propName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
    if (pi == null) throw new ArgumentOutOfRangeException("propName", string.Format("Property {0} was not found in Type {1}", propName, obj.GetType().FullName));
    return (T)pi.GetValue(obj, null);
}

/// <summary>
/// Returns a private Property Value from a given Object. Uses Reflection.
/// Throws a ArgumentOutOfRangeException if the Property is not found.
/// </summary>
/// <typeparam name="T">Type of the Property</typeparam>
/// <param name="obj">Object from where the Property Value is returned</param>
/// <param name="propName">Propertyname as string.</param>
/// <returns>PropertyValue</returns>
public static T GetPrivateFieldValue<T>(this object obj, string propName)
{
    if (obj == null) throw new ArgumentNullException("obj");
    Type t = obj.GetType();
    FieldInfo fi = null;
    while (fi == null && t != null)
    {
        fi = t.GetField(propName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
        t = t.BaseType;
    }
    if (fi == null) throw new ArgumentOutOfRangeException("propName", string.Format("Field {0} was not found in Type {1}", propName, obj.GetType().FullName));
    return (T)fi.GetValue(obj);
}

/// <summary>
/// Sets a _private_ Property Value from a given Object. Uses Reflection.
/// Throws a ArgumentOutOfRangeException if the Property is not found.
/// </summary>
/// <typeparam name="T">Type of the Property</typeparam>
/// <param name="obj">Object from where the Property Value is set</param>
/// <param name="propName">Propertyname as string.</param>
/// <param name="val">Value to set.</param>
/// <returns>PropertyValue</returns>
public static void SetPrivatePropertyValue<T>(this object obj, string propName, T val)
{
    Type t = obj.GetType();
    if (t.GetProperty(propName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) == null)
        throw new ArgumentOutOfRangeException("propName", string.Format("Property {0} was not found in Type {1}", propName, obj.GetType().FullName));
    t.InvokeMember(propName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty | BindingFlags.Instance, null, obj, new object[] { val });
}

/// <summary>
/// Set a private Property Value on a given Object. Uses Reflection.
/// </summary>
/// <typeparam name="T">Type of the Property</typeparam>
/// <param name="obj">Object from where the Property Value is returned</param>
/// <param name="propName">Propertyname as string.</param>
/// <param name="val">the value to set</param>
/// <exception cref="ArgumentOutOfRangeException">if the Property is not found</exception>
public static void SetPrivateFieldValue<T>(this object obj, string propName, T val)
{
    if (obj == null) throw new ArgumentNullException("obj");
    Type t = obj.GetType();
    FieldInfo fi = null;
    while (fi == null && t != null)
    {
        fi = t.GetField(propName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
        t = t.BaseType;
    }
    if (fi == null) throw new ArgumentOutOfRangeException("propName", string.Format("Field {0} was not found in Type {1}", propName, obj.GetType().FullName));
    fi.SetValue(obj, val);
}
Cheep answered 14/10, 2009 at 11:46 Comment(3)
Just to safe someone elses hair (that have been just pulled out on my head): this won't work in Silverlight runtimes: msdn.microsoft.com/de-de/library/xb5dd1f1%28v=vs.95%29.aspxFolie
SetValue would be better than InvokeMember, since the former supports passing indexDuck
Could you elaborate on why you used extension methods? Does it only work this way (I guess not)? In general, I would only use extension methods if there is a good reason. (For unit testing and the like it's probably ok)Viola
K
120
t.GetProperty("CreatedOn")
    .SetValue(obj, new DateTime(2009, 10, 14), null);

EDIT: Since the property itself is public, you apparently don't need to use BindingFlags.NonPublic to find it. Calling SetValue despite the the setter having less accessibility still does what you expect.

Kroon answered 14/10, 2009 at 11:42 Comment(4)
To be fair, it depends on the trust level, but the answer seems valid.Nitaniter
Property set method not found at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)Weeden
This works fine for me if I am not using a virtual property. If I SetValue with a virtual property, this does not seem to work.Joann
I have a doubt here. If the class is an abstract type. How can we create an object of it and pass it as the first parameter of SetValue?Rub
Z
13

You can access private setter from derived type via code

public static void SetProperty(object instance, string propertyName, object newValue)
{
    Type type = instance.GetType();

    PropertyInfo prop = type.BaseType.GetProperty(propertyName);

    prop.SetValue(instance, newValue, null);
}
Zacynthus answered 10/2, 2011 at 11:18 Comment(2)
+1, But just a note here. BaseType should have all of the properties you are expecting. If you are hiding a property (without remembering that you'd done so), it could result in some hair being pulled out.Fatso
Does not work, throws System.NullReferenceException : Object reference not set to an instance of an object. I have this code var dataset = new Dataset(); SetProperty(dataset, "Id", 1);Rapscallion
W
9

None of these worked for me, and my property name was unique, so I just used this:

public static void SetPrivatePropertyValue<T>(T obj, string propertyName, object newValue)
{
    // add a check here that the object obj and propertyName string are not null
    foreach (FieldInfo fi in obj.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic))
    {
        if (fi.Name.ToLower().Contains(propertyName.ToLower()))
        {
            fi.SetValue(obj, newValue);
            break;
        }
    }
}
Weeden answered 24/8, 2016 at 20:33 Comment(3)
thanks. this works for me too. i think it's because you and me are both dealing with private fields. not propertiesClavicembalo
The only downside is that setting the field of the property’s backing store doesn’t trigger the notify (for something like a WPF binding) the way a method on the same class using the CreatedOn property setter would.Weeden
I am wondering why do you need T in this method?Venture
A
0
    //mock class
    public class Person{
        public string Name{get; internal set;}
    }

    // works for all types, update private field through reflection
    public static T ReviveType<T>(T t, string propertyName, object newValue){
        // add a check here that the object t and propertyName string are not null
        PropertyInfo pi = t.GetType().GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance);
         pi.SetValue(t, newValue, null); 
        return t;
    }

    // check the required function
    void Main()
    {
        var p = new Person(){Name="John"};
        Console.WriteLine("Name: {0}",p.Name);

        //box the person to object, just to see that the method never care about what type you pass it
        object o = p;
        var updatedPerson = ReviveType<Object>(o, "Name", "Webber") as Person;

         //check if it updated person instance
        Console.WriteLine("Name: {0}",updatedPerson.Name);
    }



// Console Result: -------------------
Name: John
Name: Webber
Adele answered 15/9, 2014 at 13:5 Comment(0)
V
-1

Use

            UtilTest.SetProperty(data.Process, "CurrentStepId", (int)WorkFlowStep.HappyClaimsIsContract);

Definition

public class UtilTest
{
public static void SetProperty( object instance, string propertyName, object newValue)
    {
        try
        {
            Type type = instance.GetType();

            PropertyInfo prop = type.GetProperty(propertyName);
            if (prop.PropertyType == typeof(decimal))
            {
                prop.SetValue(instance, Convert.ToDecimal(newValue), null);

            }
            else
            {
                prop.SetValue(instance, newValue, null);
            }
        }
        catch
        {
        }

    }

}

Vertex answered 8/6, 2022 at 13:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.