2013-05-15 11 views
7

Mam obiekt ExpandoObject, który wysyłam do metody zewnętrznej biblioteki, która pobiera obiekt. Z tego, co widziałem, ta zewnętrzna lib używa wewnętrznie TypeDescriptor.GetProperties i to wydaje się powodować pewne problemy z moim ExpandoObject.Eksponowanie właściwości obiektu ExpandoObject

Mogę zamiast tego użyć anonimowego obiektu, który wydaje się działać, ale znacznie wygodniej jest używać ExpandoObject.

Czy muszę zbudować własny DynamicObject i zająć się nim osobiście, wdrażając ICustomTypeDescriptor lub coś tu brakuje.

Pomysły?


Aktualizacja

Poza Odpowiedź somedave poniżej (zgodnie z uwagami), dodałem tej klasy

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

i zarejestrował to tak:

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

Czy masz na końcu listę nazw nieruchomości? –

+0

nie przed czasem (czas kompilacji) ... stąd ExpandoObject –

Odpowiedz

9

Wdrożenie ICustomTypeDescriptor faktycznie nie jest takie trudne. Oto przykładowy kod, który zaadaptowałem z pracy, którą wykonałem z siatkami właściwości WinForms (która używa TypeDescriptor i PropertyDescriptor). Sztuką jest również zaimplementować odpowiednią klasę PropertyDescriptor, którą można cofnąć od ICustomTypeDescriptor.GetProperties(). Na szczęście ExpandoObject czyni to całkiem prostym poprzez implementację IDictionary<string, object> dla dynamicznego pobierania jego kluczy i wartości. Należy pamiętać, że może to działać nieprawidłowo (nie testowałem) i prawdopodobnie nie będzie działać dla ExpandoObjects z wieloma zagnieżdżonymi właściwościami.

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

To świetnie, ale faktycznie wysyłam mój ExpandoObject do zewnętrznej biblioteki, a ExpandoObj jest zapieczętowany, więc nie mogę go odziedziczyć. Więc jak mogę powiedzieć, aby użyć własnej implementacji ICustomTypeDescriptor, a następnie ...? –

+2

Ah, nie w pełni rozumiem problem. Myślę, że ta sztuczka stworzy jedną dodatkową klasę, implementację "TypeDescriptionProvider". Posiada metodę 'GetTypeDescriptor()' (z kilkoma przeciążeniami), która zwraca 'ICustomTypeDescriptor' dla danego obiektu lub typu. Rig to, aby zwrócić instancję 'ExpandoTypeDescriptor' powyżej. Następnie zarejestruj dostawcę za pomocą 'TypeDescriptor.AddProvider()'. Myślę, że wszystko powinno działać razem. Czy to ma sens? – daveaglick

+0

Zaznaczę twoją odpowiedź jako poprawną, ponieważ działała z rzeczami dodanymi w komentarzach ... Będę edytować moje pytanie, aby to podkreślić. tnx !! –

Powiązane problemy