我有一个ExpandoObject,我要发送给一个外部库方法,它接受一个对象。据我所见,这个外部库在内部使用TypeDescriptor.GetProperties,这似乎给我的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);发布于 2013-05-15 14:41:52
实际上,实现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; }
}
}
}发布于 2020-09-18 12:47:12
让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);
}https://stackoverflow.com/questions/16567283
复制相似问题