是否可以通过反射设置私有属性?
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
【讨论】:
以上是关于是否可以通过反射设置私有属性?的主要内容,如果未能解决你的问题,请参考以下文章