2012-03-11 12 views
14

Desidero poter accedere ai valori di proprietà in un oggetto come un dizionario, utilizzando il nome della proprietà come chiave. Non mi interessa davvero se i valori vengono restituiti come oggetti, quindi Dictionary<string, object> va bene. Questo è l'utilizzo previsto:Trattare oggetto come dizionario proprietà in C#

object person = new { Name: "Bob", Age: 45 }; 
IDictionary<string, object> lookup = new PropertyDictionary(person); 
string name = (string)person["Name"]; 
person["Age"] = (int)person["Age"] + 1; // potentially editable 

Stavo per implementare la mia classe per questo, ma poi ho iniziato a notare classi come DynamicObject implementano l'interfaccia IDictionary, che ha reso che questo era già stato fatto per me da qualche parte.

Quello che voglio è simile alla funzionalità utilizzata da ASP.NET MVC che consente di utilizzare tipi anonimi per impostare attributi di tag HTML. Ho un sacco di classi che usano dizionari come fonti di dati, ma il più delle volte dovrei essere in grado di passare anche gli oggetti.

Dato che questo è per uno scopo generale library, ho pensato di creare una classe riutilizzabile che decorasse semplicemente un oggetto con l'interfaccia IDictionary. Mi salverà dal creare un'esplosione di sovraccarichi.

+0

Qualcosa di simile indicizzazione su MSDN ... http://msdn.microsoft.com/en-us/library/6x16t2tx.aspx – Lloyd

risposta

18

non credo ci sia un built-in di tipo Net come questo già nel quadro .Net. Sembra che tu voglia veramente creare un oggetto che si comporta molto come un oggetto Javascript. Se è così allora deriva da DynamicObject potrebbe essere la scelta giusta. Ti consente di creare un oggetto che, quando viene avvolto con dynamic, ti consente di eseguire il binding direttamente su obj.Name o tramite l'indicizzatore obj["Name"].

public class PropertyBag : DynamicObject { 
    private object _source; 
    public PropertyBag(object source) { 
    _source = source; 
    } 
    public object GetProperty(string name) { 
    var type = _source.GetType(); 
    var property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); 
    return property.GetValue(_source, null); 
    } 
    public override bool TryGetMember(GetMemberBinder binder, out object result) { 
    result = GetProperty(binder.Name); 
    return true; 
    } 
    public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) { 
    result = GetProperty((string)indexes[0]); 
    return true; 
    } 
} 

È possibile utilizzare questo per avvolgere qualsiasi tipo e utilizzare sia la sintassi indicizzatore e il nome per ottenere le proprietà

var student = new Student() { FirstName = "John", LastName = "Doe" }; 
dynamic bag = new PropertyBag(student); 
Console.WriteLine(bag["FirstName"]); // Prints: John 
Console.WriteLine(bag.FirstName);  // Prints: John 
+0

Ho fatto qualcosa di simile, ma senza ereditare DynamicObject. Ho appena ereditato da IDictionary e supporta solo l'indicizzazione, poiché è tutto ciò di cui ho bisogno. –

+0

Hai guardato ExpandoObject? http://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject.aspx – Scott

+1

ExpandoObject funziona solo se si sta partendo da zero. –

1

parola chiave dinamica può essere un'opzione per voi. utilizza il runtime del linguaggio dinamico. In fase di runtime, cerca di abbinare il tipo più vicino disponibile nel programma. Se non riesce, converte il tipo dinamico in oggetto dictionay, dove key è il nome della proprietà e value è il valore della proprietà.

seguire questi collegamenti di MSDN:

Using dynamic keyword in C#
dynamic (C# Reference)
DLR Overview
usage of dynamic sample walkthough page

+0

L'unico problema è che avrei avuto bisogno di modificare tutti i miei implementazioni ad accettare una dinamica. Inoltre, non sono sicuro di come fare riferimento a una proprietà per nome, poiché obj ["Nome"] verrà interpretato come una chiamata a un indicizzatore sulla dinamica. –

+0

non puoi andare direttamente al dizionario? avere la tua implementazione può rendere le cose incasinate ... Ciò che sottintende è che vuoi una classe che viene automaticamente convertita in dizionario tramite l'indicizzatore? hai provato a creare l'indicizzatore con parametro di tipo stringa? – Uday0119

+0

Voglio racchiudere un oggetto, non copiarlo su un dizionario. L'idea è di passare un oggetto dove è previsto un IDictionary. In questo modo posso usare i tipi anonimi in cui sono previsti i dizionari. –

8

ho questo metodo di estensione, probabilmente il più semplice si può ottenere:

public static Dictionary<string, object> ToPropertyDictionary(this object obj) 
{ 
    var dictionary = new Dictionary<string, object>(); 
    foreach (var propertyInfo in obj.GetType().GetProperties()) 
     if (propertyInfo.CanRead && propertyInfo.GetIndexParameters().Length == 0) 
      dictionary[propertyInfo.Name] = propertyInfo.GetValue(obj, null); 
    return dictionary; 
} 

Ora si può fare:

object person = new { Name = "Bob", Age = 45 }; 
var lookup = person.ToPropertyDictionary(); 
string name = (string)lookup["Name"]; 
lookup["Age"] = (int)lookup["Age"] + 1; // indeed editable 

Nota:

  1. che questo dizionario è case-sensitive (si può banalmente estenderlo passando il diritto StringComparer).

  2. che ignora gli indicizzatori (che sono anche proprietà) ma tocca a te lavorarci sopra.

  3. che il metodo non è generico considerando che non aiuta il pugilato perché internamente chiama obj.GetType, quindi lo stesso box a quel punto.

  4. che si ottengono solo le proprietà "leggibili" (altrimenti non si ottiene il valore in esso contenuto). Dato che si desidera che sia anche "scrivibile", è necessario utilizzare anche il flag CanWrite.

Problemi correlati