使用Reflection从基类构造函数设置派生类的值

时间:2011-07-21 19:17:41

标签: c# .net reflection

我有两个这样的课程:

public abstract class MyBase
{
     protected MyBase(){
         Initialize();
     }

     protected IDictionary<string,string> _data;

     private void Initialize() {
         // Use Reflection to get all properties
         // of the derived class (e.g., call new MyDerived() then
         // I want to know the names "Hello" and "ID" here
         var data = GetDataFromBackend(propertyNamesFromDerived);
         _data = data;
     }
}

public class MyConcrete : MyBase
{
     public MyConcrete(){
         // Possibly use Reflection here
         Hello = _data["Hello"];
         ID = new Guid(data["ID"]);
     }

     public string Hello {get;set;}
     public Guid ID {get; set;}
}

如您所见,我希望我的基类的构造函数知道我正在实例化的派生类的属性。

现在,这似乎是一个巨大的代码气味,所以让我提供更多关于我的意图的背景,也许还有更好的方法。

我有一个存储Key / Value Pairs的后端系统,基本上是Dictionary<string,string>。我想抽象使用这个后端系统,人们可以创建属性为后端系统的键的类。当他们构造这个对象时,它会自动从该系统加载数据并将所有变量初始化为它。

换句话说,我刚刚彻底改造了序列化,除了我不控制后端系统而只是让它真的无痛。我不希望调用者在构造对象后调用Initialize(),因为在100%的情况下,你必须在构建之后将其初始化。

我不想将初始化代码移动到派生类中,但字符串到业务对象的转换除外。

我必须使用工厂吗?或者在基础构造函数中查看派生类的属性 names 是否安全? (不要关心他们的价值观,他们没有初始化,只需要名字)。

或者是否有更好的方法在字符串字典和具体业务对象之间提供外观?

编辑:这是.net 3.5,所以没有System.Dynamic可以解决这个问题:(

编辑2:在查看了答案之后再考虑一下这些问题,我想我的问题现在归结为:现在从基础构造函数中调用GetType()。GetProperties()获取属性的名称以及是否使用某个属性安全装饰?

6 个答案:

答案 0 :(得分:5)

等等,让我们在这里停一秒钟并正确地做到这一点。 执行此操作不应该MyBase

所以你写了一个类来管理你从后端获取东西,然后在那个类上编写一个类似

的方法
T Get<T>() where T : new()

并且让Get负责从后端读取字典并使用反射来填充T的实例。因此,你说

var concrete = foo.Get<MyConcrete>();

这并不难,而且这是正确的方法。

顺便提一下,Get的代码看起来像是

T t = new T();
var properties = typeof(T).GetProperties();
foreach(var property in properties) {
    property.SetValue(t, dictionary[property.Name], null);
}
return t;

其中dictionary是您加载的键/值对。事实证明,有更好的方法可以做到这一点,但除非它是一个瓶颈,否则我不会担心它。

答案 1 :(得分:3)

更好的方法是让类直接使用字典:

public string Hello {
    get { return (string)base.data["Hello"]; }
    set { base.data["Hello"] = value; }
}

您可能希望在getter中调用TryGetValue,以便在键不存在时可以返回默认值。 (您可能应该在基类的单独方法中执行此操作)

您可以制作代码段,以便更轻松地创建属性。

如果您不想这样做,可以致电GetType().GetProperties()获取班级中属性的PropertyInfo个对象,然后拨打SetValue(this, value)
这会很慢;您可以使用各种技巧来使用表达式树CreateDelegate或IL生成来加速它。

答案 2 :(得分:1)

答案 3 :(得分:0)

您是否考虑过使用 ExpandoObject ?有了它,您可以动态添加属性并检查它们(例如,在序列化时)。

答案 4 :(得分:0)

我不确定这是不是你应该做的,但这就是你要求的(把它放在Initialize中,你会得到一个派生属性名列表):

        var derivedProps = this.GetType().GetProperties();
        var propNames = new List<string>(derivedProps.Select(x => x.Name));

从那里开始,使用PropertyInfo中的derivedProps,您可以设置属性。

答案 5 :(得分:0)

无论如何,你无法真正安全地对基类构造函数中的那些属性做任何事情,因为一些派生构造函数无论如何都可能重置它们。你做两相加载要好得多(例如,明确地调用Initialize)