是否可以通过反射设置私有属性?

Posted

技术标签:

【中文标题】是否可以通过反射设置私有属性?【英文标题】:Is it possible to set private property via reflection? 【发布时间】:2010-12-06 15:34:11 【问题描述】:

我可以通过反射设置私有属性吗?

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

我尝试了以下方法,但它不起作用,其中t 表示Entity 的类型:

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

我想我能做到,但我做不出来。

【问题讨论】:

我知道这已经晚了,但我发现需要这个想法,我会分享我的“为什么”。我需要克服一些第三方软件带来​​的不便。具体来说,我使用的是 Crystal Reports ExportToStream 方法。该方法的编写方式不允许访问流的内部缓冲区。为了将报告发送到浏览器,我必须将流复制到一个新的缓冲区(100K+),然后将其发送出去。通过将流对象中的私有“_exposable”字段设置为“true”,我能够直接发送内部缓冲区,为每个请求节省 100K+ 的分配。 为什么?假设您在所有域对象的 Id 属性上都有私有设置器,并且您想要实现存储库测试。然后,只有在您的存储库测试项目中,您才希望能够设置 Id 属性。 另一种使用场景:在导入数据时设置“创建日期”等自动生成的字段。 另一个为什么我只是好奇它是否可能。这就是我最终查看此问题的方式。 【参考方案1】:

是的,它是:

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

【讨论】:

只是为了保护别人的头发(我头上刚刚拔掉的头发):这在 Silverlight 运行时不起作用:msdn.microsoft.com/de-de/library/xb5dd1f1%28v=vs.95%29.aspx SetValue 会比 InvokeMember 更好,因为前者支持传递索引 你能详细说明你为什么使用扩展方法吗?它只能以这种方式工作吗(我猜不是)?一般来说,如果有充分的理由,我只会使用扩展方法。 (对于单元测试之类的可能没问题)【参考方案2】:
t.GetProperty("CreatedOn")
    .SetValue(obj, new DateTime(2009, 10, 14), null);

编辑:由于该属性本身是公开的,您显然不需要使用BindingFlags.NonPublic 来查找它。尽管设置器的可访问性较低,但调用 SetValue 仍然可以达到您的预期。

【讨论】:

公平地说,这取决于信任级别,但答案似乎是有效的。 在 System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo 文化中找不到属性集方法) 如果我不使用虚拟财产,这对我来说很好。如果我使用虚拟属性设置值,这似乎不起作用。 我有一个疑问。如果类是抽象类型。我们如何创建它的对象并将其作为 SetValue 的第一个参数传递?【参考方案3】:

您可以通过代码从派生类型访问私有 setter

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

【讨论】:

+1,但这里只是一个注释。 BaseType 应该具有您期望的所有属性。如果你隐藏了一个属性(不记得你已经这样做了),它可能会导致一些头发被拉出。 不起作用,抛出System.NullReferenceException : Object reference not set to an instance of an object.我有这个代码var dataset = new Dataset(); SetProperty(dataset, "Id", 1);【参考方案4】:

这些都不适合我,而且我的属性名称是唯一的,所以我只使用了这个:

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;
        
    

【讨论】:

它有效,谢谢【参考方案5】:
    //mock class
    public class Person
        public string Nameget; 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

【讨论】:

以上是关于是否可以通过反射设置私有属性?的主要内容,如果未能解决你的问题,请参考以下文章

反射学习3-通过反射机制修改类中的私有属性的值

Java反射设置和访问私有属性值

java反射机制可以调用到私有方法,是否就破坏了JAVA的卦装性呢。

面向对象,封装,反射,绑定方法

.NET 通过反射获取私有属性

转载: 通过反射操作类的私有属性