2010-10-14 32 views
22

Estoy explorando el modelo DynamicObject en .NET 4.0. La aplicación es aquella en la que se describirá un objeto a través de algún tipo de archivo de texto/xml, y el programa debe crear un objeto al leer ese archivo.Agregar miembros a un objeto dinámico en tiempo de ejecución

Con DynamicObject, podemos agregar miembros fácilmente, dado que sabemos el nombre del miembro a priori. Pero, ¿qué pasa si ni siquiera sabemos el nombre del miembro para agregar? ¿Hay alguna manera de hacer esa dinámica también?

Por ejemplo, supongamos que necesito crear un objeto con los miembros 'Propiedad1', 'Propiedad2' y otro objeto con 'PropiedadA' y 'PropiedadB' como se describe en el archivo de texto/XML. ¿Cómo puedo crear un objeto de forma dinámica en función de esta información?

ACTUALIZACIÓN Tengo algunas ideas de este post: http://www.codeproject.com/KB/cs/dynamicincsharp.aspx

Esta aplicación me permite hacer algo como lo siguiente:

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

La razón por la que no deseo usar un diccionario es para hacer uso de los métodos TryGetMember y TrySetMember que puedo anular, dentro de los cuales puedo generar eventos que son esenciales para el programa.

De esta manera, puedo heredar de una clase base (PFChannel), pero también puedo agregar miembros sobre la marcha. Pero mi problema es que no sabré el nuevo nombre de propiedad hasta el tiempo de ejecución. Y, en realidad, no creo que el objeto dinámico me permita agregar nuevas propiedades sobre la marcha. Si este es el caso, ¿cómo puedo usar ExpandoObject para darme esta habilidad?

+2

Aún no entiende WHY dynamic y ExpandoObject entraron en .NET. La razón era exactamente lo contrario de lo que quieres. ExpandoObject todavía usa Dictionary , y es IS Dictionary. El compilador solo traduce todas las llamadas de "propiedad" a Dictionary [NameOfProperty]. Esto hace que el código sea mucho más agradable. Pero eso es todo, es solo código-azúcar. Lo que quiere se puede lograr con el diccionario incluso sin tipear dinámicamente. – Euphoric

+0

¿Qué pasa con los eventos que deben plantearse como elementos del diccionario? – sbenderli

+0

posible duplicación de [Agregar miembros dinámicamente a un objeto dinámico] (http: // stackoverflow.com/questions/2079870/dynamically-adding-members-to-a-dynamic-object) – nawfal

Respuesta

37

Si solo necesita hacer eso, debe consultar ExpandoObject. Si necesita hacer eso y sigue usando DynamicObject, tendrá que escribir el código para recordar los valores de las propiedades, básicamente ... lo que podría hacer con un ExpandoObject incrustado.

Sin embargo, no tengo claro qué es lo que quiere hacer con este objeto después: ¿está seguro de que necesita algún tipo de tipado dinámico? ¿Sería un Dictionary<string, object> realmente peor? Depende de lo que va a consumir el objeto más tarde, básicamente.

+0

Bueno, el objeto realmente necesita heredar una clase base. La clase base define numerosos eventos y propiedades que son esenciales para el resto del programa. Una de las razones por las que estoy interesado en DynamicObject es que puedo anular los métodos TryGetMember y TrySetMember, dentro de los cuales necesito generar eventos. Pero, ¿cómo sería una pieza de código que agrega un miembro cuyo nombre no se conoce a priori? – sbenderli

+0

@sbenderli - Entonces, simplemente puede crear un nuevo objeto que se deriva de su clase base y este objeto puede usar Dictionary para mantener la información sobre las propiedades "desconocidas". – Euphoric

+0

Consulte mi actualización – sbenderli

6

No estoy seguro de que quiera usar un objeto dinámico en este caso.

dinámica en C# le permite hacer cosas como:

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

Si utiliza un ExpandoObject, puede:

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

Ambos permitirá utilizar el propertyname como si realmente existe en tiempo de compilación. En su caso, no necesitará usar esta sintaxis para nada, así que no estoy seguro de que le brinde ningún beneficio. En su lugar, ¿por qué no usar un Dictionary<string, object>, y:

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

Debido a que un Dictionary<string,object> es básicamente lo que es un objeto expando - pero tiene soporte para una sintaxis diferente. Sí, estoy simplificando, pero si no está usando esto desde otro código o mediante enlace/etc, esto es básicamente cierto.

+0

Por favor, mira mi actualización – sbenderli

0

tipo dynamic se implementa básicamente como Property Bag. Eso significa su diccionario de claves (nombres de propiedad) y objetos (aquí en modo no seguro). No uso si puede acceder directamente a este diccionario, pero puede usar el diccionario usted mismo.

Maldición, Jon Skeet era más rápido :(

25

De acuerdo con esto: Adding properties and methods to an ExpandoObject, dynamically!,

... puede utilizar un objeto expando como el titular de su valor, y lo echó a un IDictionary < cadena, objeto > cuando se desea agregar propiedades con nombres dinámicos.

Ejemplo

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 se prueba y se imprimirá "verdadero" en la pantalla de la consola.

Por supuesto, en su caso, donde su objeto subyacente tiene que heredar de otra clase, este ejemplo se da solo para darle una idea de una posible implementación personalizada.

¿Quizás incluir un objeto expandido en su implementación de clase y redirigir llamadas a tryget y tryset a la instancia del objeto expando en su clase?

ACTUALIZACIÓN

SI su clase base se deriva de DynamicObject (lo que significa que puede anular todas TrySet/Obtener/invocar métodos) a continuación, también se puede usar un diccionario interno. En las anulaciones try get/set, realizará cualquier disparo de evento que desee y delegará la configuración al diccionario interno.

Para agregar una nueva propiedad (o eliminar una existente) puede anular TryInvoke. Cuando el nombre de mothod es, por ejemplo, "AddProperty" y hay un argumento de tipo string, entonces debería agregar un nuevo elemento en su diccionario con el nombre del argumento. Del mismo modo, definiría dinámicamente un "RemoveProperty", etc. Ni siquiera necesita un objeto expando.

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. 

    } 
} 

Por supuesto, como ya he dicho, que funcionaría sólo si su clase base se deriva de DynamicObject.

1

En relación con la respuesta Thanasis Ioannidis en relación con una variante "Si su clase base se deriva de DynamicObject", hay una manera más sencilla, sólo tiene que añadir método AddProperty en clase "MyClass" de esta manera:

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

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

A continuación, puede usar

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

No necesita reemplazar la función "TryInvoke".

Cuestiones relacionadas