Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/326.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 公开ExpandooObject的属性_C#_.net_Dynamic_Expandoobject - Fatal编程技术网

C# 公开ExpandooObject的属性

C# 公开ExpandooObject的属性,c#,.net,dynamic,expandoobject,C#,.net,Dynamic,Expandoobject,我有一个ExpandoObject,我将它发送到一个外部库方法,它接受一个对象。据我所见,这个外部库在内部使用TypeDescriptor.GetProperties,这似乎会导致ExpandooObject出现一些问题 public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) { var defaultDescriptor = base.GetTypeDescri

我有一个ExpandoObject,我将它发送到一个外部库方法,它接受一个对象。据我所见,这个外部库在内部使用TypeDescriptor.GetProperties,这似乎会导致ExpandooObject出现一些问题

public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
{
     var defaultDescriptor = base.GetTypeDescriptor(objectType, instance);

     return instance == null ? defaultDescriptor :
            new ExpandoTypeDescriptor((ExpandoObject)instance);
}
我可以用一个匿名对象来代替,这似乎很有效,但使用ExpandoObject对我来说更方便

public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
{
     var defaultDescriptor = base.GetTypeDescriptor(objectType, instance);

     return instance == null ? defaultDescriptor :
            new ExpandoTypeDescriptor((ExpandoObject)instance);
}
我是否需要构造自己的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<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; }
        }
    }
}
公共类ExpandoTypeDescriptor:ICustomTypeDescriptor
{
私有只读ExpandoObject_expando;
公共ExpandoTypeDescriptor(ExpandoObject expando)
{
_expando=expando;
}
//对于其中的大多数,只需使用TypeDescriptor的默认行为即可
//不过,这可能需要一些调整才能正确用于ExpandoObjects。。。
公共字符串GetComponentName()
{
返回TypeDescriptor.GetComponentName(此为true);
}
公共事件描述符GetDefaultEvent()
{
返回TypeDescriptor.GetDefaultEvent(this,true);
}
公共字符串GetClassName()
{
返回TypeDescriptor.GetClassName(此为true);
}
公共事件描述符集合GetEvents(属性[]属性)
{
返回TypeDescriptor.GetEvents(this,attributes,true);
}
EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents()
{
返回TypeDescriptor.GetEvents(this,true);
}
公共类型转换器GetConverter()
{
返回TypeDescriptor.GetConverter(此为true);
}
公共对象GetPropertyOwner(PropertyDescriptor pd)
{
返回_expando;
}
公共属性集合GetAttributes()
{
返回TypeDescriptor.GetAttributes(this,true);
}
公共对象GetEditor(类型editorBaseType)
{
返回TypeDescriptor.GetEditor(this,editorBaseType,true);
}
公共属性描述程序GetDefaultProperty()
{
返回null;
}
//这就是GetProperties()调用的位置
//暂时忽略该属性,如果需要,则必须实现支持
//应该足够简单的使用。。。
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
{
返回((ICustomTypeDescriptor)this.GetProperties(新属性[0]);
}
公共属性DescriptorCollection GetProperties(属性[]属性)
{
//这只是将ExpandoObject强制转换为IDictionary以获取键
返回新属性DescriptorCollection(
((IDictionary)_expando).键
.选择(x=>new ExpandoPropertyDescriptor(((IDictionary)u expando),x))
.ToArray());
}
//一个嵌套的PropertyDescriptor类,可以获取和设置
//在运行时动态扩展对象
私有类ExpandoPropertyDescriptor:PropertyDescriptor
{
专用只读IDictionary _expando;
私有只读字符串\u名称;
公共ExpandoPropertyDescriptor(IDictionary expando,字符串名称)
:base(名称,null)
{
_expando=expando;
_名称=名称;
}
公共覆盖类型PropertyType
{
获取{return\u expando[\u name].GetType();}
}
公共覆盖无效设置值(对象组件、对象值)
{
_expando[_name]=值;
}
公共覆盖对象GetValue(对象组件)
{
返回_expando[_name];
}
公共覆盖布尔为只读
{
得到
{
//您可以在这里实现一些更好的逻辑
返回false;
}
}
公共重写类型ComponentType
{
获取{return null;}
}
公共覆盖布尔CanResetValue(对象组件)
{
返回false;
}
公共替代无效重置值(对象组件)
{
}
公共重写bool ShouldSerializeValue(对象组件)
{
返回false;
}
公共重写字符串类别
{
获取{return string.Empty;}
}
公共重写字符串描述
{
获取{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);
}

你有一个属性名列表吗?没有提前(编译时)…因此ExpandoObject很好,但实际上我正在将我的ExpandoObject发送到外部库,ExpandoObj是密封的,所以我不能从中继承。那么我如何告诉它使用我自己的ICustomTypeDescriptor实现呢…?啊,我没有完全理解这个问题。我认为关键在于创建一个额外的类,一个
TypeDescriptionProvider
实现。它有一个方法
GetTypeDescriptor()