¿Cómo reflexiono sobre los miembros del objeto dinámico?

131

¿Necesito obtener un diccionario de propiedades y sus valores de un objeto declarado con la palabra clave dinámica en .NET 4? Parece que usar la reflexión para esto no funcionará.

Ejemplo:

dynamic s = new ExpandoObject();
s.Path = "/Home";
s.Name = "Home";

// How do I enumerate the Path and Name properties and get their values?
IDictionary<string, object> propertyValues = ???
Flatliner DOA
fuente

Respuestas:

32

Si IDynamicMetaObjectProvider puede proporcionar los nombres de miembros dinámicos, puede obtenerlos. Consulte la implementación de GetMemberNames en la biblioteca PCL con licencia de Apache Dynamitey (que se puede encontrar en nuget), funciona para ExpandoObjectsy DynamicObjects que implementanGetDynamicMemberNames y cualquier otro IDynamicMetaObjectProviderque proporciona un objeto meta con una implementación de GetDynamicMemberNamessin allá de las pruebas personalizadais IDynamicMetaObjectProvider .

Después de obtener los nombres de los miembros, es un poco más de trabajo obtener el valor de la manera correcta, pero Impromptu hace esto, pero es más difícil señalar solo las partes interesantes y tener sentido. Aquí está la documentación y es igual o más rápido que la reflexión, sin embargo, es poco probable que sea más rápido que una búsqueda de diccionario para expando, pero funciona para cualquier objeto, expando, dinámico u original, lo que sea.

jbtule
fuente
17
Gracias por llegar al punto, el código es: var tTarget = target como IDynamicMetaObjectProvider; if (tTarget! = null) {tList.AddRange (tTarget.GetMetaObject (Expression.Constant (tTarget))). GetDynamicMemberNames ()); }
Flatliner DOA
gracias por tu gran biblioteca Estaba teniendo problemas para disparar un objeto dinámico a esta biblioteca dynamicjson.codeplex.com , y pude extenderlo con su biblioteca.
JJS
1
ejemplo por favor.
Demodave
105

En el caso de ExpandoObject, la clase ExpandoObject realmente implementa IDictionary<string, object>sus propiedades, por lo que la solución es tan trivial como la conversión:

IDictionary<string, object> propertyValues = (IDictionary<string, object>)s;

Tenga en cuenta que esto no funcionará para objetos dinámicos generales. En estos casos, deberá desplegar el DLR a través de IDynamicMetaObjectProvider.

itowlson
fuente
Gracias por eso, desafortunadamente la muestra estaba un poco simplificada. Necesito poder inspeccionar un objeto dinámico sin saber cuál es su tipo real.
Flatliner DOA
2
Esto solo funciona para objetos de la clase ExpandoObject, la clase DynamicObject es otra clase extensible que no implementa IDictionary, sino que implementa IDynamicMetaObjectProvider.
Sharique Abdullah
1
Sin embargo, responde la pregunta del OP.
Sharique Abdullah
58

Hay varios escenarios a considerar. En primer lugar, debe verificar el tipo de su objeto. Simplemente puede llamar a GetType () para esto. Si el tipo no implementa IDynamicMetaObjectProvider, puede usar la misma reflexión que para cualquier otro objeto. Algo como:

var propertyInfo = test.GetType().GetProperties();

Sin embargo, para las implementaciones de IDynamicMetaObjectProvider, la reflexión simple no funciona. Básicamente, necesita saber más sobre este objeto. Si es ExpandoObject (que es una de las implementaciones de IDynamicMetaObjectProvider), puede usar la respuesta proporcionada por itowlson. ExpandoObject almacena sus propiedades en un diccionario y simplemente puede convertir su objeto dinámico en un diccionario.

Si se trata de DynamicObject (otra implementación de IDynamicMetaObjectProvider), debe utilizar los métodos que expone este DynamicObject. DynamicObject no está obligado a "almacenar" su lista de propiedades en ninguna parte. Por ejemplo, podría hacer algo como esto (estoy reutilizando un ejemplo de mi publicación de blog ):

public class SampleObject : DynamicObject
{
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = binder.Name;
        return true;
    }
}

En este caso, cada vez que intente acceder a una propiedad (con cualquier nombre), el objeto simplemente devuelve el nombre de la propiedad como una cadena.

dynamic obj = new SampleObject();
Console.WriteLine(obj.SampleProperty);
//Prints "SampleProperty".

Por lo tanto, no tiene nada sobre lo que reflexionar: este objeto no tiene ninguna propiedad y, al mismo tiempo, funcionarán todos los nombres de propiedad válidos.

Diría que para las implementaciones de IDynamicMetaObjectProvider, debe filtrar las implementaciones conocidas donde puede obtener una lista de propiedades, como ExpandoObject, e ignorar (o lanzar una excepción) para el resto.

Alexandra Rusina
fuente
77
Simplemente me parece que si el tipo que consumo es dinámico, entonces no debería hacer suposiciones sobre su tipo subyacente. Debería poder llamar a GetDynamicMemberNames para obtener la lista de miembros. ¡Parece estúpido que los tipos estáticos tengan mejor soporte de inspección de tiempo de ejecución que los dinámicos!
Flatliner DOA
2
Puede anular el método GetDynamicMemberNames () para que un objeto dinámico devuelva los nombres de lista de miembros dinámicos. El problema es que no se garantiza que cada objeto dinámico tenga este método (ExpandoObject no). No es sorprendente que la reflexión funcione mejor para los tipos estáticos. Fue creado para ellos en primer lugar. Con la dinámica, debe confiar más en pruebas unitarias y TDD.
Alexandra Rusina
1
La documentación de MSDN para GetDynamicMemberNames () menciona: "Este método existe solo para fines de depuración", no realmente reconfortante :(
NicoJuicy
19

Requiere Newtonsoft Json.Net

Un poco tarde, pero se me ocurrió esto. Le da solo las claves y luego puede usarlas en la dinámica:

public List<string> GetPropertyKeysForDynamic(dynamic dynamicToGetPropertiesFor)
{
    JObject attributesAsJObject = dynamicToGetPropertiesFor;
    Dictionary<string, object> values = attributesAsJObject.ToObject<Dictionary<string, object>>();
    List<string> toReturn = new List<string>();
    foreach (string key in values.Keys)
    {
        toReturn.Add(key);                
    }
    return toReturn;
}

Entonces simplemente predicas así:

foreach(string propertyName in GetPropertyKeysForDynamic(dynamicToGetPropertiesFor))
{
    dynamic/object/string propertyValue = dynamicToGetPropertiesFor[propertyName];
    // And
    dynamicToGetPropertiesFor[propertyName] = "Your Value"; // Or an object value
}

Eligiendo obtener el valor como una cadena u otro objeto, o hacer otra dinámica y usar la búsqueda nuevamente.

Señor b
fuente
No necesita la lista para volver.
aloisdg se muda a codidact.com el
44
¡Perfecto! Tuve que cambiar los atributos JObjectAsJObject = dynamicToGetPropertiesFor; a los atributos del objetoAsJObject = (JObject) JToken.FromObject (obj); ¡¡aunque!!
k25
Solo para reiterar lo que dijo @ k25. Es necesario sustituir JObject attributesAsJObject = dynamicToGetPropertiesFor;con algo como: var jObject = (JObject) JToken.FromObject(dynamicToGetPropertiesFor);. En ese punto, puede obtener un diccionario de nombres y valores de propiedades haciendo algo como var objProperties = jObject.ToObject<Dictionary<string, object>>();. Con eso en mano, te vas a las carreras. Esto no necesita una dinámica. Funciona bien con cualquier cosa que sea una subclase deDynamicObject
Flydog57