.NET 反射设置私有属性

22
如果您有一个像这样定义的属性:
private DateTime modifiedOn;
public DateTime ModifiedOn
{
    get { return modifiedOn; }
}
Reflection如何设置对象的特定属性值?
我已经尝试过以下两种方法:
dto.GetType().GetProperty("ModifiedOn").SetValue(dto, modifiedOn, null);

并且

dto.GetType().GetProperty("modifiedOn").SetValue(dto, modifiedOn, null);

但是没有取得任何成功。如果这是一个愚蠢的问题,那么很抱歉,因为这是我第一次使用C#.NET反射。

6个回答

41

这个属性没有setter方法;你需要:

public DateTime ModifiedOn
{
    get { return modifiedOn; }
    private set {modifiedOn = value;}
}

(你可能需要使用BindingFlags——我马上试一下)

如果没有setter,你只能依靠模式/字段名称(这很脆弱),或者解析IL(非常困难)。

以下代码可以正常工作:

using System;
class Test {
    private DateTime modifiedOn;
    public DateTime ModifiedOn {     
        get { return modifiedOn; }
        private set { modifiedOn = value; }
    }
}
static class Program {
    static void Main() {
        Test p = new Test();
        typeof(Test).GetProperty("ModifiedOn").SetValue(
            p, DateTime.Today, null);
        Console.WriteLine(p.ModifiedOn);
    }
}

它也适用于自动实现属性:

public DateTime ModifiedOn { get; private set; }

(如果仅依赖于字段名称会出现严重问题)


很酷,我没有想到可以在setter前面加上private。我想我得检查一下那些BindingFlags。谢谢。 - Lieven Cardoen
4
如果整个属性都是私有的,那么似乎只需要使用“BindingFlags”。 - Marc Gravell
还是不行,我猜我得使用那些 BindingFlags? - Lieven Cardoen
定义“不起作用” - 会发生什么?我发布的示例运行良好...另外:你是在使用CF吗?Silverlight?还是普通的.NET?(这很重要...) - Marc Gravell
稍等一下,可能忘记了什么。 - Lieven Cardoen
由于getter是公共的,您应该将"ModifiedOn"替换为nameof(Test.ModifiedOn)。这样一来,属性重命名就不会导致运行时异常。 - Timo

4
你可以尝试设置后备字段而不是属性;你应该使用GetField()而不是GetProperty()

1
虽然这样做可以起作用,但访问字段通常是反射的一种更加脆弱的形式。例如,当有人发现C# 3.0并将其设置为public DateTime ModifiedOn { get; private set; }时会发生什么? - Marc Gravell
如果您确实想设置后备字段,为了最小化出错的可能性,我建议尝试找到以下三种可能性之一:(1)相同名称,忽略大小写。(2)下划线+相同名称,忽略大小写。(3)自动属性后备字段的默认命名方案(在SO上很容易找到)。 - Timo
请注意,GetField()有一个重载函数可以忽略大小写。 - Timo

3
如果您的属性没有setter方法,您就无法调用SetValue方法对其进行赋值。

1
你需要设置字段,因为你没有设置属性来设置该属性。此外,需要使用BindingFlags.NonPublic来处理非公共对象。
dto.GetType().
    GetField("modifiedOn", 
    BindingFlags.NonPublic|BindingFlags.SetField|BindingFlags.Instance).
    SetValue(dto, valueToSet);

0
如果您有一个带有setter的私有属性,那么您可以使用这个扩展方法来设置值:
using System.Reflection;

public static class ObjectExtensions
{
    public static void SetPrivateValue<T>(this T obj, string propertyName, object value)
    {
        var type = typeof(T);
        type.GetTypeInfo().GetDeclaredProperty(propertyName).SetValue(obj, value, null);
    }
}

0

有一种方法可以做到这一点,这可能是最正确的方法,考虑到set可能存在或不存在,就是使用特定的访问器。

var myc = new MyClass();
var pi = typeof(MyClass).GetProperty("Prop1", BindingFlags.NonPublic | BindingFlags.Instance);

if (pi.SetMethod != null) // check if you have 'set' accessor
    pi.SetMethod.Invoke(myc, new object[]{ someValue });
else
{
    // do nothing OR
    throw new Exception("Attempted to set read-only property " + pi.Name);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接