繁体   English   中英

使用反射设置来自基本 class 构造函数的派生 class 的值

[英]Setting Values on a derived class from the base class constructor using 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;}
}

如您所见,我希望我的基础 class 的构造函数了解我正在实例化的派生 class 的属性。

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

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

换句话说,我刚刚重新发明了序列化,只是我不控制后端系统,只是让使用它变得非常轻松。 我不希望调用者在构建 object 后必须调用Initialize() ,因为在 100% 的情况下,您必须在构建后对其进行初始化。

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

我必须使用工厂吗? 或者在基本构造函数中查看派生 class 的属性名称是否安全? (不要关心它们的值并且它们没有被初始化,只需要名称)。

或者有没有更好的方法在字符串字典和具体业务 object 之间提供一个外观?

编辑:这是 .net 3.5,所以没有 System.Dynamic 这会使这变得微不足道:(

编辑 2:在查看了答案并进一步思考之后,我想我的问题现在真的可以归结为:是否从基本构造函数调用 GetType().GetProperties() 以获取属性名称以及它们是否都装饰有一定的属性保险箱?

等等,让我们在这里稍等片刻,并正确执行此操作。 这样做不应该是MyBase的责任。

因此,您编写了一个 class 来为您管理从后端获取内容的方法,然后在该 class 上编写一个类似于

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是您加载的键/值对。 事实证明有更优化的方法可以做到这一点,但除非它是一个瓶颈,否则我不会担心它。

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

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

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

您可以制作代码片段以使属性更易于创建。

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

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

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

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

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

无论如何,您不能真正安全地对基类构造函数中的这些属性执行任何操作,因为某些派生构造函数可能会重置它们。 你最好进行两阶段加载(例如显式调用初始化)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM