公开ExpandoObject的属性

本文关键字:属性 ExpandoObject 公开 | 更新日期: 2023-09-27 18:13:37

我有一个ExpandoObject,我发送给一个外部库方法,它接受一个对象。从我所看到的来看,这个外部库使用了TypeDescriptor。这似乎会导致一些问题与我的ExpandoObject。

我可以用一个匿名对象,这似乎工作,但它更方便我使用ExpandoObject。

我需要构造我自己的DynamicObject并通过实现ICustomTypeDescriptor来照顾它吗?还是我在这里遗漏了什么?

想法?


除了下面一些人的回答(根据评论),我添加了这个类

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);

公开ExpandoObject的属性

实现ICustomTypeDescriptor实际上并没有那么难。以下是我从WinForms属性网格(使用TypeDescriptor和PropertyDescriptor)中改编的一些示例代码。诀窍是还实现一个适当的PropertyDescriptor类,您可以从ICustomTypeDescriptor.GetProperties()传递回来。值得庆幸的是,ExpandoObject通过实现IDictionary<string, object>来动态检索它的键和值,使这个过程变得非常简单。请记住,这可能会或可能不会正确工作(我还没有测试过),它可能不会工作的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<string, object> to get the keys
        return new PropertyDescriptorCollection(
            ((IDictionary<string, object>)_expando).Keys
            .Select(x => new ExpandoPropertyDescriptor(((IDictionary<string, object>)_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<string, object> _expando;
        private readonly string _name;
        public ExpandoPropertyDescriptor(IDictionary<string, object> 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; }
        }
    }
}

我可以让OP的代码与Marc的ExpandoTypeDescriptor代码一起工作的唯一方法是修改OP对GetTypeDescriptor的调用,将返回值转换为ExpandoObject。

public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
{
     var defaultDescriptor = base.GetTypeDescriptor(objectType, instance);
     return instance == null ? defaultDescriptor :
            new ExpandoTypeDescriptor((ExpandoObject)instance);
}