148

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.

Alessio Cantarella
  • 5,077
  • 3
  • 27
  • 34
AwkwardCoder
  • 24,893
  • 27
  • 82
  • 152
  • 2
    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. – Ray Dec 06 '09 at 23:47
  • 21
    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. – bounav Jun 17 '10 at 15:44
  • 2
    Another use scenario: setting auto-generated fields like "creation date" when importing data. – ANeves Nov 12 '14 at 18:50
  • Another why is I'm just curious if it's possible. That's how I ended up viewing this question. – Caleb Mauer Feb 01 '20 at 03:22

6 Answers6

135

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);
}
spottedmahn
  • 14,823
  • 13
  • 108
  • 178
Arthur
  • 7,939
  • 3
  • 29
  • 46
  • 7
    Just to safe someone elses hair (that have been just pulled out on my head): this won't work in Silverlight runtimes: http://msdn.microsoft.com/de-de/library/xb5dd1f1%28v=vs.95%29.aspx – Marc Wittke Apr 20 '12 at 10:04
  • SetValue would be better than InvokeMember, since the former supports passing index – Chris Xue Jun 14 '16 at 13:30
  • 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) – Jakob Sep 13 '21 at 08:38
118
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.

Tinister
  • 11,097
  • 6
  • 35
  • 36
  • 6
    To be fair, it depends on the trust level, but the answer seems valid. – Marc Gravell Dec 12 '09 at 12:27
  • 13
    Property set method not found at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture) – CZahrobsky Aug 24 '16 at 20:08
  • 1
    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. – JonathanPeel May 25 '17 at 18:12
  • 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? – Nikhil Banka Jul 22 '21 at 16:12
12

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);
}
Siarhei Kuchuk
  • 5,296
  • 1
  • 28
  • 31
  • +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. – ouflak Sep 23 '16 at 06:24
  • 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);` – Muflix Jan 13 '22 at 19:49
6

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;
        }
    }
}
CZahrobsky
  • 762
  • 7
  • 7
  • thanks. this works for me too. i think it's because you and me are both dealing with private fields. not properties – symbiont May 17 '23 at 10:48
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
Biniam Eyakem
  • 545
  • 5
  • 18
-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
        {
        }

    }

}

Go Man
  • 67
  • 1
  • 3