使用。net 4, c#
假设我有一个类Info
,它扩展了CustomTypeDescriptor
。Info
类的实例有一个<string, object>
对的字典,在运行时加载。
我希望能够将字典键作为属性公开(以便Info
的每个实例具有不同的属性)。属性的值应该是字典中相应的值。
我开始暴露属性:
public override PropertyDescriptorCollection GetProperties()
{
var orig = base.GetProperties();
var newProps = dictionary.Select( kvp =>
TypeDescriptor.CreateProperty(
this.GetType(),
kvp.key,
typeof(string)));
return new PropertyDescriptorCollection(
orig.Cast<PropertyDescriptor>()
.Concat(newProps)
.ToArray());
}
问题是,我如何得到它们的值?
var info = new Info(new Dictionary<string, object>{{"some_property", 5}};
var prop = TypeDescriptor.GetProperties(i_info)["some_property"];
var val = prop.GetValue(i_info); //should return 5
当prop.GetValue()
被调用时,我发现获得控制的唯一方法是覆盖GetPropertyOwner(PropertyDescriptor pd)
,但我理解它的方式是,它希望我返回另一个类型的实例,该实例具有匹配的真实(编译)属性。
我希望能够自己编写属性的实际实现(在本例中,返回字典中键与属性名称匹配的值)。
这可能吗?
您需要自己实现覆盖GetValue
方法的PropertyDescriptor
类。所以代替TypeDescriptor.CreateProperty
,你将使用新的MyCoolPropertyDescriptor(dictionary, kvp.Key)
或类似的。
下面是如何实现它的示例:
<>之前
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
namespace ConsoleApplication1
{
internal sealed class MyCoolPropertyDescriptor : PropertyDescriptor
{
private Func<object, object> propertyGetter;
private Action<object, object> propertySetter;
public MyCoolPropertyDescriptor(
string name,
Func<object, object> propertyGetter,
Action<object, object> propertySetter)
: base(name, new Attribute[] {})
{
this.propertyGetter = propertyGetter;
this.propertySetter = propertySetter;
}
public override bool CanResetValue(object component)
{
return true;
}
public override System.Type ComponentType
{
get { return typeof(object); }
}
public override object GetValue(object component)
{
return this.propertyGetter(component);
}
public override bool IsReadOnly
{
get { return false; }
}
public override System.Type PropertyType
{
get { return typeof(object); }
}
public override void ResetValue(object component)
{
this.propertySetter(component, null);
}
public override void SetValue(object component, object value)
{
this.propertySetter(component, value);
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
}
public sealed class Info : CustomTypeDescriptor
{
IDictionary<string, object> properties;
public Info(IDictionary<string, object> properties)
{
this.properties = properties;
}
public override PropertyDescriptorCollection GetProperties()
{
var orig = base.GetProperties();
var newProps = this.properties
.Select(kvp => new MyCoolPropertyDescriptor(
kvp.Key,
o => ((Info)o).properties[kvp.Key],
(o, v) => ((Info)o).properties[kvp.Key] = v));
return new PropertyDescriptorCollection(orig
.Cast<PropertyDescriptor>()
.Concat(newProps)
.ToArray());
}
}
internal class Program
{
private static void Main(string[] args)
{
var info = new Info(new Dictionary<string, object>{{"some_property", 5}});
var prop = TypeDescriptor.GetProperties(info)["some_property"];
var val = prop.GetValue(info); //should return 5
Console.WriteLine(val);
}
}
}
我对CustomTypeDescriptor
的理解是,它允许数据绑定将额外的属性暴露给,比如说,一个网格,而这些属性实际上并不存在于类中。它不是扩展CLR的东西,所以你的实际类公开属性。
如果你想要实际的CLR属性,那么你需要查看DynamicObject
或ExpandoObject
来获得我认为你想要的那种功能。