公开ExpandoObject的属性

我有一个ExpandoObject,我发送到一个外部库方法,它接受一个对象。 从我看到的这个外部lib内部使用TypeDescriptor.GetProperties,这似乎导致我的ExpandoObject的一些问题。

我可以改为使用匿名对象,这似乎有效但我使用ExpandoObject更方便。

我是否需要构建自己的DynamicObject并通过实现ICustomTypeDescriptor自己处理它,或者我在这里遗漏了一些东西。

想法?


更新

除了以下somedave的答案(根据评论),我添加了这个类

public class ExpandoObjectTypeDescriptionProvider : TypeDescriptionProvider { private static readonly TypeDescriptionProvider m_Default = TypeDescriptor.GetProvider(typeof(ExpandoObject)); public ExpandoObjectTypeDescriptionProvider() :base(m_Default) { } public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) { var defaultDescriptor = base.GetTypeDescriptor(objectType, instance); return instance == null ? defaultDescriptor : new ExpandoObjectTypeDescriptor(instance); } } 

并注册如下:

 dynamic parameters = new ExpandoObject(); TypeDescriptor.AddProvider(new ExpandoObjectTypeDescriptionProvider(), parameters); 

实现ICustomTypeDescriptor实际上并不是那么难。 下面是我使用WinForms属性网格(使用TypeDescriptor和PropertyDescriptor)进行的一些工作改编的示例代码。 诀窍是还要实现一个适当的PropertyDescriptor类,您可以从ICustomTypeDescriptor.GetProperties()传回来。 值得庆幸的是,ExpandoObject通过实现IDictionary来动态检索它的键和值,从而使这变得非常简单。 请记住,这可能会或可能无法正常工作(我还没有测试过),它可能不适用于具有大量嵌套属性的ExpandoObjects。

 public class ExpandoTypeDescriptor : ICustomTypeDescriptor { private readonly ExpandoObject _expando; public ExpandoTypeDescriptor(ExpandoObject expando) { _expando = expando; } // Just use the default behavior from TypeDescriptor for most of these // This might need some tweaking to work correctly for ExpandoObjects though... public string GetComponentName() { return TypeDescriptor.GetComponentName(this, true); } public EventDescriptor GetDefaultEvent() { return TypeDescriptor.GetDefaultEvent(this, true); } public string GetClassName() { return TypeDescriptor.GetClassName(this, true); } public EventDescriptorCollection GetEvents(Attribute[] attributes) { return TypeDescriptor.GetEvents(this, attributes, true); } EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents() { return TypeDescriptor.GetEvents(this, true); } public TypeConverter GetConverter() { return TypeDescriptor.GetConverter(this, true); } public object GetPropertyOwner(PropertyDescriptor pd) { return _expando; } public AttributeCollection GetAttributes() { return TypeDescriptor.GetAttributes(this, true); } public object GetEditor(Type editorBaseType) { return TypeDescriptor.GetEditor(this, editorBaseType, true); } public PropertyDescriptor GetDefaultProperty() { return null; } // This is where the GetProperties() calls are // Ignore the Attribute for now, if it's needed support will have to be implemented // Should be enough for simple usage... PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() { return ((ICustomTypeDescriptor)this).GetProperties(new Attribute[0]); } public PropertyDescriptorCollection GetProperties(Attribute[] attributes) { // This just casts the ExpandoObject to an IDictionary to get the keys return new PropertyDescriptorCollection( ((IDictionary)_expando).Keys .Select(x => new ExpandoPropertyDescriptor(((IDictionary)_expando), x)) .ToArray()); } // A nested PropertyDescriptor class that can get and set properties of the // ExpandoObject dynamically at run time private class ExpandoPropertyDescriptor : PropertyDescriptor { private readonly IDictionary _expando; private readonly string _name; public ExpandoPropertyDescriptor(IDictionary expando, string name) : base(name, null) { _expando = expando; _name = name; } public override Type PropertyType { get { return _expando[_name].GetType(); } } public override void SetValue(object component, object value) { _expando[_name] = value; } public override object GetValue(object component) { return _expando[_name]; } public override bool IsReadOnly { get { // You might be able to implement some better logic here return false; } } public override Type ComponentType { get { return null; } } public override bool CanResetValue(object component) { return false; } public override void ResetValue(object component) { } public override bool ShouldSerializeValue(object component) { return false; } public override string Category { get { return string.Empty; } } public override string Description { get { return string.Empty; } } } }