c# - Agregar miembros a un objeto dinámico en tiempo de ejecución
.net dynamic (5)
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.
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?
ACTUALIZAR Tengo algunas ideas de esta publicación: http://www.codeproject.com/KB/cs/dynamicincsharp.aspx
Esta implementació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 utilizar un diccionario es hacer uso de los métodos TryGetMember y TrySetMember que puedo anular, dentro de los cuales puedo plantear 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?
De acuerdo con esto: ¡ Agregar propiedades y métodos a un ExpandoObject, dinámicamente! ,
... puede usar un objeto expando como su titular de valor, y convertirlo en un IDictionary <cadena, objeto> cuando desee agregar propiedades con nombre dinámico.
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
Esto 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 proporciona solo para darle una idea de una posible implementación personalizada.
¿Tal vez incluir un objeto expando en su implementación de clase y redireccionar llamadas para probar y probar la instancia del objeto expando en su clase?
ACTUALIZAR
SI su clase base se deriva de DynamicObject (lo que significa que puede anular todos los métodos TrySet / Get / Invoke), también puede usar un diccionario internamente. 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 dije, eso funcionaría solo si su clase base deriva de DynamicObject.
No estoy seguro de que quiera usar un objeto dinámico en este caso.
dynamic in c # te permite hacer cosas como:
dynamic something = GetUnknownObject();
something.aPropertyThatShouldBeThere = true;
Si usa un ExpandoObject
, puede:
var exp = GetMyExpandoObject();
exp.APropertyThatDidntExist = "something";
Ambos le permiten usar el nombre de propiedad como si realmente existiera en el momento de la 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 cambio, ¿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;
}
Porque 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.
Relacionado con la respuesta de Thanasis Ioannidis con respecto a una variante "Si su clase base se deriva de DynamicObject", hay una manera más simple, solo agregue el método AddProperty en la 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;
}
Entonces puedes usar
dynamic myObject = new MyClass();
myObject.AddProperty("DateOfBirth", new DateTime(1980,23,11));
No necesita reemplazar la opción "TryInvoke".
Si solo necesitas hacer eso, deberías mirar ExpandoObject
. Si necesita hacer eso y aún usar DynamicObject
, necesitará escribir 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 alguna clase de escritura dinámica? ¿Un Dictionary<string, object>
realidad sería peor? Depende de lo que va a consumir el objeto más tarde, básicamente.
dynamic
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 fue más rápido :(