我有两个这样的课程:
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()获取属性的名称以及是否使用某个属性安全装饰?
答案 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)