根据您的评论:“我想要一种简单且编译时安全的语法来获取有关成员的信息”。
这是一个经常被请求的功能,并且已经在 C# 团队的会议上讨论了大约十年,但从未被优先考虑到足以被包括在内。
这篇博文解释了原因:
http://blogs.msdn.com/ericlippert/archive/2009/05/21/in-foof-we-trust-a-dialogue.aspx
所以现在,您只需要与缺失的功能作斗争。也许您可以发布有关您的更广泛问题的更多信息,看看人们是否可以提出不同的方法。
更新
如果没有关于您的问题的更多信息,这只是猜测。但是,如果您有一个表示值但还带有附加“元”信息的属性,则您始终可以将其表示为新类型并使用“注入”步骤来设置所有内容。
以下是此类“元属性”的建议抽象接口:
public interface IMetaProperty<TValue>
{
TValue Value { get; set; }
string DisplayName { get; }
event Action<TValue, TValue> ValueChanged;
}
属性的值只是另一个子属性,其类型由用户定义。
我已经输入了显示名称,并且作为奖励,您有一个在值更改时触发的事件(因此您可以免费获得“可观察性”)。
要在一个类中拥有这样的属性,你可以这样声明它:
public class SomeClass
{
public IMetaProperty<string> FirstName { get; private set; }
public IMetaProperty<string> LastName { get; private set; }
public IMetaProperty<int> Age { get; private set; }
public SomeClass() { MetaProperty.Inject(this); }
}
注意属性上的设置器是如何私有的。这可以防止任何人意外设置属性本身而不是设置 Value 子属性。
所以这意味着类必须设置这些属性,使它们不仅仅是null。它通过调用一个神奇的Inject 方法来做到这一点,该方法可以在任何类上工作:
public static class MetaProperty
{
// Make it convenient for us to fill in the meta information
private interface IMetaPropertyInit
{
string DisplayName { get; set; }
}
// Implementation of a meta-property
private class MetaPropertyImpl<TValue> : IMetaProperty<TValue>,
IMetaPropertyInit
{
private TValue _value;
public TValue Value
{
get { return _value; }
set
{
var old = _value;
_value = value;
ValueChanged(old, _value);
}
}
public string DisplayName { get; set; }
public event Action<TValue, TValue> ValueChanged = delegate { };
}
public static void Inject(object target)
{
// for each meta property...
foreach (var property in target.GetType().GetProperties()
.Where(p => p.PropertyType.IsGenericType &&
p.PropertyType.GetGenericTypeDefinition()
== typeof(IMetaProperty<>)))
{
// construct an implementation with the correct type
var impl = (IMetaPropertyInit)
typeof (MetaPropertyImpl<>).MakeGenericType(
property.PropertyType.GetGenericArguments()
).GetConstructor(Type.EmptyTypes).Invoke(null);
// initialize any meta info (could examine attributes...)
impl.DisplayName = property.Name;
// set the value
property.SetValue(target, impl, null);
}
}
}
它只是使用反射找到隐藏在对象中的所有IMetaProperty槽,并用一个实现填充它们。
所以现在SomeClass 的用户可以说:
var sc = new SomeClass
{
FirstName = { Value = "Homer" },
LastName = { Value = "Simpson" },
Age = { Value = 38 },
};
Console.WriteLine(sc.FirstName.DisplayName + " = " + sc.FirstName.Value);
sc.Age.ValueChanged += (from, to) =>
Console.WriteLine("Age changed from " + from + " to " + to);
sc.Age.Value = 39;
// sc.Age = null; compiler would stop this
如果您已经在使用 IOC 容器,您也许可以在不直接进行反射的情况下实现其中的一些。