2010-10-14 1 views
22

Sto esplorando il modello DynamicObject in .NET 4.0. L'applicazione è quella in cui un oggetto verrà descritto attraverso una sorta di file text/xml e il programma deve creare un oggetto dopo aver letto quel file.Aggiunta di membri a un oggetto dinamico in fase di esecuzione

Con DynamicObject, possiamo aggiungere facilmente membri, dato che conosciamo il nome del membro a priori. Ma cosa succede se non sappiamo nemmeno il nome del membro da aggiungere? C'è un modo per renderlo dinamico?

Ad esempio, supponiamo di dover creare un oggetto con Proprietà1, "Proprietà2" e un altro oggetto con "ProprietàA" e "ProprietàB" come descritto dal file testo/XML. Come posso creare un oggetto dinamicamente in base a queste informazioni?

UPDATE Ho alcune idee da questo post: http://www.codeproject.com/KB/cs/dynamicincsharp.aspx

Questa implementazione mi permette di fare qualcosa di simile al seguente:

dynamic d = new PFDynamicChannel(); 
PFCouplings c = ((PFChannel)d).Coupling; 
d.NewProperty = "X"; 

Il motivo per cui non voglio usare un dizionario è utilizzare i metodi TryGetMember e TrySetMember che è possibile sovrascrivere, all'interno del quale è possibile generare eventi essenziali per il programma.

In questo modo, posso ereditare da una classe base (PFChannel), ma posso anche aggiungere membri al volo. Ma il mio problema è che non conoscerò il nuovo nome della proprietà fino al runtime. E, in realtà non penso che l'oggetto dinamico mi permetta di aggiungere nuove proprietà al volo. Se questo è il caso, come posso usare ExpandoObject per darmi questa capacità?

+2

Ancora non capisci PERCHE 'dynamic e ExpandoObject sono entrati in .NET. Il motivo era esatto opposto a quello che volevi. ExpandoObject usa ancora Dictionary , beh IS Dictionary. Il compilatore traduce tutte le chiamate di "proprietà" solo nel dizionario [NameOfProperty]. Questo rende il codice molto più bello. Ma questo è tutto, il suo unico codice-zucchero. Quello che vuoi può essere acquisito con il dizionario anche senza alcuna digitazione dinamica. – Euphoric

+0

E gli eventi che devono essere generati quando vengono impostati gli elementi del dizionario? – sbenderli

+0

possibile duplicato di [Aggiunta dinamica di membri a un oggetto dinamico] (http: // stackoverflow.it/questions/2079870/dynamically-adding-members-to-a-dynamic-object) – nawfal

risposta

37

Se si solo è necessario farlo, si dovrebbe guardare ExpandoObject. Se è necessario fare ciò e utilizzare ancora DynamicObject, sarà necessario scrivere il codice per ricordare i valori delle proprietà, in pratica ... che si potrebbe potenzialmente fare con uno ExpandoObject incorporato.

Non mi è chiaro cosa vuoi fare con questo oggetto in seguito, sei sicuro di aver bisogno di una digitazione dinamica? Sarebbe un peggio di Dictionary<string, object>? Dipende da cosa sta per consumare l'oggetto in un secondo momento, in fondo.

+0

Bene, l'oggetto ha effettivamente bisogno di ereditare una baseclass. La classe di base definisce numerosi eventi e proprietà essenziali per il resto del programma. Uno dei motivi per cui sono interessato a DynamicObject è che posso ignorare i metodi TryGetMember e TrySetMember, all'interno dei quali ho bisogno di aumentare gli eventi. Ma come sarebbe un pezzo di codice che aggiunge un membro il cui nome non è noto a priori? – sbenderli

+0

@sbenderli - Quindi puoi semplicemente creare un nuovo oggetto che deriva dalla tua cella di base e questo oggetto può usare Dictionary per mantenere le informazioni sulle proprietà "sconosciute". – Euphoric

+0

Si prega di consultare il mio aggiornamento – sbenderli

6

Non sono sicuro di voler utilizzare un oggetto dinamico in questo caso.

dinamica in C# consente di fare cose come:

dynamic something = GetUnknownObject(); 
    something.aPropertyThatShouldBeThere = true; 

Se si utilizza un ExpandoObject, è possibile:

var exp = GetMyExpandoObject(); 
    exp.APropertyThatDidntExist = "something"; 

Entrambi questi consentono di utilizzare il propertyname come se in realtà esiste al momento della compilazione. Nel tuo caso, non avrai affatto bisogno di usare questa sintassi, quindi non sono sicuro che ti darebbe alcun vantaggio. Invece, perché non basta usare un Dictionary<string, object>, e:

var props = new Dictionary<string, object>(); 
    foreach(var someDefinintion in aFile) 
    { 
     props[ someDefinition.Name] = someDefinition.value; 
    } 

Perché un Dictionary<string,object> è fondamentalmente ciò che un oggetto expando è - ma ha il supporto per una sintassi diversa. Sì, sto semplificando - ma se non lo stai usando da altro codice o tramite binding/etc, allora questo è fondamentalmente vero.

+0

Si prega di consultare il mio aggiornamento – sbenderli

0

dynamic tipo è implementato in modo basico come Borsa di proprietà. Ciò significa il suo dizionario di Chiavi (nomi di proprietà) e oggetti (qui in modo non protetto dal tipo). Non posso usare Iam se puoi accedere direttamente a questo dizionario, ma puoi usare il dizionario da solo.

Accidenti, Jon Skeet è stato più veloce :(

25

Secondo questa: Adding properties and methods to an ExpandoObject, dynamically!,

... è possibile utilizzare un oggetto expando come titolare valore, e gettalo via a un IDictionary < stringa, oggetto > quando si desidera aggiungere le proprietà denominate in modo dinamico.

Esempio

dynamic myobject = new ExpandoObject(); 

IDictionary<string, object> myUnderlyingObject = myobject; 

myUnderlyingObject.Add("IsDynamic", true); // Adding dynamically named property 

Console.WriteLine(myobject.IsDynamic); // Accessing the property the usual way 

Th è testato e stamperà "true" sullo schermo della console.

Naturalmente, nel tuo caso, dove l'oggetto sottostante deve ereditare da un'altra classe, questo esempio viene fornito solo per darti un'idea per una potenziale implementazione personalizzata.

Forse include un oggetto expando nell'implementazione di classe e il reindirizzamento delle chiamate a tryget e tryset all'istanza dell'oggetto expando nella classe?

UPDATE

se la classe di base deriva da DynamicObject (significa che è possibile ignorare tutto TrySet/Get/metodi Richiamare), allora, si potrebbe anche utilizzare un dizionario internamente. Nel tentativo get/set esegue l'override di qualsiasi evento che si attiva e delegare l'impostazione al dizionario interno.

Per aggiungere una nuova proprietà (o rimuoverne una esistente), è possibile ignorare TryInvoke. Quando il nome del mothod è, ad esempio, "AddProperty" e c'è un argomento di tipo string, si dovrebbe aggiungere un nuovo elemento nel dizionario con il nome dell'argomento. Allo stesso modo si definirà dinamicamente una "RemoveProperty" ecc. Non è nemmeno necessario un expando object.

class MyBaseClass: DynamicObject 
{ 
    // usefull functionality 
} 

class MyClass: MyBaseClass 
{ 
    Dictionary<string, object> dynamicProperties = new Dictionary<string, object>(); 

    override bool TryGetMember(...) 
    { 
     // read the value of the requested property from the dictionary 
     // fire any events and return 
    } 

    override bool TrySetMember(...) 
    { 
     // set the value of the requested property to the dictionary 
     // if the property does not exist, 
     // add it to the dictionary (compile time dynamic property naming) 
     // fire any events 
    } 

    override bool TryInvoke(...) 
    { 
     // check what method is requested to be invoked 
     // is it "AddProperty"?? 
     // if yes, check if the first argument is a string 
     // if yes, add a new property to the dictionary 
     // with the name given in the first argument (runtime dynamic property naming) 
     // if there is also a second argument of type object, 
     // set the new property's value to that object. 

     // if the method to be invoked is "RemoveProperty" 
     // and the first argument is a string, 
     // remove from the Dictionary the property 
     // with the name given in the first argument. 

     // fire any events 
    } 
} 

// USAGE 
static class Program 
{ 
    public static void Main() 
    { 
     dynamic myObject = new MyClass(); 

     myObject.FirstName = "John"; // compile time naming - TrySetMember 
     Console.WriteLine(myObject.FirstName); // TryGetMember 

     myObject.AddProperty("Salary"); // runtime naming (try invoke "AddProperty" with argument "Salary") 
     myObject.Salary = 35000m; 
     Console.WriteLine(myObject.Salary); // TryGetMember 

     myObject.AddProperty("DateOfBirth", new DateTime(1980,23,11)); // runtime naming (try invoke "AddProperty" with fisrt argument "DateOfBirth" and second argument the desired value) 
     Console.WriteLine(myObject.DateOfBirth); // TryGetMember 

     myObject.RemoveProperty("FirstName"); // runtime naming (try invoke "RemoveProperty" with argument "FirstName") 

     Console.WriteLine(myObject.FirstName); // Should print out empty string (or throw, depending on the desired bahavior) because the "FirstName" property has been removed from the internal dictionary. 

    } 
} 

Naturalmente, come ho già detto, che avrebbe funzionato solo se la classe di base deriva da DynamicObject.

1

correlati alla risposta Thanasis Ioannidis per quanto riguarda una variante "Se la classe di base deriva da DynamicObject", c'è un modo più semplice, è sufficiente aggiungere il metodo AddProperty in "MyClass" classe come questa:

class MyClass: MyBaseClass 
    { 
     Dictionary<string, object> dynamicProperties = new Dictionary<string, object>(); 

     public void AddProperty(string key, object value){ 
      dynamicProperties[key] = value; 
     } 

Poi si possibile utilizzare

dynamic myObject = new MyClass(); 
myObject.AddProperty("DateOfBirth", new DateTime(1980,23,11)); 

Non è necessario eseguire l'override su "TryInvoke".