简体   繁体   English

公开 ExpandoObject 的属性

[英]Exposing properties of an ExpandoObject

I've got an ExpandoObject that I'm sending to an external library method which takes an object.我有一个 ExpandoObject,我将它发送到一个接受对象的外部库方法。 From what I've seen this external lib uses TypeDescriptor.GetProperties internally and that seems to cause some problems with my ExpandoObject.从我所见,这个外部库在内部使用 TypeDescriptor.GetProperties,这似乎给我的 ExpandoObject 造成了一些问题。

I could go with an anonymous object instead and that seems to work but it much more convenient for me to use the ExpandoObject.我可以改用匿名对象,这似乎可行,但使用 ExpandoObject 对我来说更方便。

Do I need to construct my own DynamicObject and take care of it myself by implementing ICustomTypeDescriptor or am I missing something here.我是否需要构建我自己的 DynamicObject 并通过实现 ICustomTypeDescriptor 自己处理它,或者我在这里遗漏了什么。

Ideas?想法?


Update更新

Besides the answer by somedave below (as per the comments), I added this class除了下面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);
    }
}

and registered it like this:并像这样注册它:

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

Implementing ICustomTypeDescriptor actually isn't all that hard.实现 ICustomTypeDescriptor 实际上并不是那么难。 Here is some sample code I adapted from some work I did with WinForms property grids (which uses TypeDescriptor and PropertyDescriptor).下面是一些示例代码,我改编自我使用 WinForms 属性网格(使用 TypeDescriptor 和 PropertyDescriptor)所做的一些工作。 The trick is to also implement an appropriate PropertyDescriptor class that you can pass back from ICustomTypeDescriptor.GetProperties() .诀窍是还可以实现一个适当的 PropertyDescriptor 类,您可以从ICustomTypeDescriptor.GetProperties()传回该类。 Thankfully the ExpandoObject makes this pretty simple by implementing IDictionary<string, object> for dynamic retrieval of it's keys and values.幸运的是,ExpandoObject 通过实现IDictionary<string, object>来动态检索它的键和值IDictionary<string, object>使这变得非常简单。 Keep in mind that this may or may not work correctly (I haven't tested it) and it probably won't work for ExpandoObjects with lots of nested properties.请记住,这可能会也可能不会正常工作(我还没有测试过),并且它可能不适用于具有大量嵌套属性的 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; }
        }
    }
}

The only way I could get the OP's code to work with Marc's ExpandoTypeDescriptor code was to modify the OP's call to GetTypeDescriptor, casting the return value to ExpandoObject.我可以让 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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM