2013-05-15 26 views
7

オブジェクトを受け取る外部ライブラリメソッドに送信するExpandoObjectがあります。私はこの外部libがTypeDescriptor.GetPropertiesを内部的に使用していることから、ExpandoObjectにいくつかの問題が発生しているようです。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); 
+0

最後にプロパティ名のリストがありますか? –

+0

事前に時間がないので(コンパイル時)... ExpandoObject –

答えて

9

実際にICustomTypeDescriptorを実装することはそれほど難しいことではありません。 WinFormsのプロパティグリッド(TypeDescriptorとPropertyDescriptorを使用する)で行ったいくつかの作業から順応したサンプルコードです。そのトリックは、ICustomTypeDescriptor.GetProperties()から返すことができる適切なPropertyDescriptorクラスを実装することです。ありがたいことに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; } 
     } 
    } 
} 
+0

これは素晴らしいですが、実際にExpandoObjectを外部ライブラリに送り、ExpandoObjが封印されているので、それを継承することはできません。それでは、ICustomTypeDescriptorの独自の実装を使用するように指示するにはどうすればよいですか? –

+2

ああ、私は問題を完全に理解していませんでした。私はこのトリックがもう一つのクラス、 'TypeDescriptionProvider'実装を作成しようとしていると思います。与えられたオブジェクトや型の 'ICustomTypeDescriptor'を返す' GetTypeDescriptor() 'メソッド(いくつかのオーバーロードを持つメソッド)を持っています。上記の 'ExpandoTypeDescriptor'のインスタンスを返すリグ。次に、TypeDescriptor.AddProvider()を使用してプロバイダを登録します。私はそれが一緒に働くすべてを得るべきだと思います。それは理にかなっていますか? – daveaglick

+0

あなたがコメントに追加したものとうまくいったので、私はあなたの答えを正しいものとしてマークします...それを強調するために質問を編集します。 tnx !! –

関連する問題