Obtener el valor de la propiedad dinámica de c # a través de una cadena


182

Me gustaría acceder al valor de una dynamicpropiedad de C # con una cadena:

dynamic d = new { value1 = "some", value2 = "random", value3 = "value" };

¿Cómo puedo obtener el valor de d.value2 ("random") si solo tengo "value2" como una cadena? En javascript, podría hacer d ["value2"] para acceder al valor ("random"), pero no estoy seguro de cómo hacerlo con c # y la reflexión. Lo más cerca que he venido es esto:

d.GetType().GetProperty("value2") ... pero no sé cómo obtener el valor real de eso.

Como siempre, gracias por tu ayuda!


26
Tenga en cuenta que este no es el propósito previsto de "dinámico" y que este escenario no funciona mejor con "dinámico" que con "objeto". "dinámico" hace posible acceder a las propiedades cuando el nombre de la propiedad se conoce en tiempo de compilación pero el tipo no. Como no conoce el nombre ni el tipo en tiempo de compilación, dinámico no lo ayudará.
Eric Lippert


3
@EricLippert Sé que esta pregunta es antigua, pero solo para hacer un comentario en caso de que alguien la vea en el futuro. En algunos casos, no puede elegir si usar objetos dinámicos u objetos (por ejemplo, cuando usa el analizador JSON) y es posible que desee obtener las propiedades de una cadena (de un archivo de configuración, por ejemplo), por lo que este uso no es tan inusual como uno podría pensar inicialmente.
Pedrom

Respuestas:


217

Una vez que tenga su PropertyInfo(desde GetProperty), debe llamar GetValuey pasar la instancia de la que desea obtener el valor. En tu caso:

d.GetType().GetProperty("value2").GetValue(d, null);

44
Estoy recibiendo un mensaje 'd.GetType().GetProperty("value2").GetValue(d)' threw an exception of type 'System.Reflection.TargetInvocationException' dynamic {System.Reflection.TargetInvocationException}en la ventana del reloj con eso ...?
TimDog

66
Think GetValue necesita un parámetro adicional: egdGetType (). GetProperty ("value2"). GetValue (d, null)
dommer

3
¿Funcionará esto en un verdadero ExpandoObject dinámico en lugar de un tipo anónimo? Dado que new {}crea un tipo anónimo real con propiedades definidas, llamar a GetType / GetProperty tiene sentido, pero qué pasa con ExpandoObject, que si llama a GetType, obtendrá un tipo que tiene las propiedades de ExpandoObject, pero no necesariamente sus propiedades dinámicas.
Triynko

16
-1. Esto solo funciona con objetos .NET simples que se convirtieron en dinámicos. No funcionará con ningún objeto dinámico personalizado como Expando o ViewBag usado ASP.NET MVC
Philipp Munin

8
esto es lo que funciona con el objeto Expando: (((IDictionary <string, object>) x)) ["value1"]
Michael Bahig el

39
public static object GetProperty(object target, string name)
{
    var site = System.Runtime.CompilerServices.CallSite<Func<System.Runtime.CompilerServices.CallSite, object, object>>.Create(Microsoft.CSharp.RuntimeBinder.Binder.GetMember(0, name, target.GetType(), new[]{Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(0,null)}));
    return site.Target(site, target);
}

Agregar referencia a Microsoft.CSharp. Funciona también para tipos dinámicos y propiedades y campos privados.

Editar : Si bien este enfoque funciona, hay un método casi 20 veces más rápido del ensamblado Microsoft.VisualBasic.dll :

public static object GetProperty(object target, string name)
{
    return Microsoft.VisualBasic.CompilerServices.Versioned.CallByName(target, name, CallType.Get);
}

2
Solo quería mencionar que la versión de VisualBasic no es equivalente a su versión original 'GetProperty' (GetProperty en realidad invoca el GetMember dinámico, que funciona incluso en objetos Python en IronPython).
Trevor Sundberg

¿Cuál sería el objetivo del objeto?
Demodave

@Demodave El objeto sobre el que desea invocar la propiedad ( den la pregunta).
IllidanS4 quiere que Monica regrese el

➕1 esto funcionó para propiedades privadas cuando FastMember y HyperDescriptor no lo harían
Chris Marisic

@ IllidanS4 cuando comparó el CallSitecódigo frente al CallByNamecódigo, ¿comparó los dos al almacenar en caché la CallSiteinstancia? Sospecho que el costo de su primer método es casi puramente la activación de Bindery CallSite, no la invocación deTarget()
Chris Marisic

24

Dynamitey es una .net stdbiblioteca de código abierto , que le permite llamarla como la dynamicpalabra clave, pero usando una cadena para el nombre de la propiedad en lugar del compilador que lo hace por usted, y termina siendo igual a la velocidad de reflexión (que no es tan rápido) como el uso de la palabra clave dinámica, pero esto se debe a la sobrecarga adicional de almacenamiento en caché de forma dinámica, donde el compilador almacena en caché de forma estática).

Dynamic.InvokeGet(d,"value2");

11

El método más fácil para obtener a settery a getterpara una propiedad que funciona para cualquier tipo, incluido dynamicy ExpandoObjectes usar, FastMemberque también es el método más rápido (usa Emit).

Puede obtener una TypeAccessorbase de un tipo determinado o una ObjectAccessorbase de una instancia de un tipo determinado.

Ejemplo:

var staticData = new Test { Id = 1, Name = "France" };
var objAccessor = ObjectAccessor.Create(staticData);
objAccessor["Id"].Should().Be(1);
objAccessor["Name"].Should().Be("France");

var anonymous = new { Id = 2, Name = "Hilton" };
objAccessor = ObjectAccessor.Create(anonymous);
objAccessor["Id"].Should().Be(2);
objAccessor["Name"].Should().Be("Hilton");

dynamic expando = new ExpandoObject();
expando.Id = 3;
expando.Name = "Monica";
objAccessor = ObjectAccessor.Create(expando);
objAccessor["Id"].Should().Be(3);
objAccessor["Name"].Should().Be("Monica");

var typeAccessor = TypeAccessor.Create(staticData.GetType());
typeAccessor[staticData, "Id"].Should().Be(1);
typeAccessor[staticData, "Name"].Should().Be("France");

typeAccessor = TypeAccessor.Create(anonymous.GetType());
typeAccessor[anonymous, "Id"].Should().Be(2);
typeAccessor[anonymous, "Name"].Should().Be("Hilton");

typeAccessor = TypeAccessor.Create(expando.GetType());
((int)typeAccessor[expando, "Id"]).Should().Be(3);
((string)typeAccessor[expando, "Name"]).Should().Be("Monica");

8

La mayoría de las veces, cuando solicita un objeto dinámico, obtiene un Objeto de Expando (no en el ejemplo de la pregunta anónimo, pero estáticamente escrito anteriormente, pero menciona JavaScript y mi analizador JSON elegido JsonFx, por ejemplo, genera ExpandoObjects).

Si su dinámica es, de hecho, un Objeto de Expando, puede evitar la reflexión lanzándola a IDictionary, como se describe en http://msdn.microsoft.com/en-gb/library/system.dynamic.expandoobject.aspx .

Una vez que ha enviado a IDictionary, tiene acceso a métodos útiles como .Item y .ContainsKey


Desafortunadamente, tener que convertir a IDictionary y usar TryGetValue, por ejemplo, da como resultado que se devuelva un objeto antiguo simple. No puede aprovechar los operadores implícitos en ese punto, ya que solo se consideran en tiempo de compilación. Por ejemplo, si tuviera una clase Int64Proxy con conversión implícita a Int64 ?, Int64? i = data.value; //data is ExpandoObjectbuscaría automáticamente y llamaría al operador implícito. Por otro lado, si tuviera que usar IDictionary para probar si existe el campo "valor", obtendría un objeto que no se lanzará sin error a Int64.
Triynko

5

GetProperty / GetValue no funciona para datos Json, siempre genera una excepción nula, sin embargo, puede probar este enfoque:

Serialice su objeto usando JsonConvert:

var z = Newtonsoft.Json.JsonConvert.DeserializeObject(Convert.ToString(request));

Luego, acceda directamente a él, volviéndolo a la cadena:

var pn = (string)z["DynamicFieldName"];

Puede funcionar directamente aplicando Convert.ToString (solicitud) ["DynamicFieldName"], sin embargo, no lo he probado.


2
Este método genera el error: error CS0021: No se puede aplicar la indexación con [] a una expresión de tipo 'objeto'. Úselo new JavaScriptSerializer().Deserialize<object>(json);para llegar a las "propiedades" de la manera que sugirió
Kris Kilton,

4

d.GetType (). GetProperty ("value2")

devuelve un objeto PropertyInfo.

Entonces hazlo

propertyInfo.GetValue(d)

2
gracias, esta fue la respuesta correcta, pero como se mencionó anteriormente, la GetValue(d)necesidad debe serGetValue(d,null)
TimDog

4

Esta es la forma en que obtuve el valor de un valor de propiedad de una dinámica:

    public dynamic Post(dynamic value)
    {            
        try
        {
            if (value != null)
            {
                var valorCampos = "";

                foreach (Newtonsoft.Json.Linq.JProperty item in value)
                {
                    if (item.Name == "valorCampo")//property name
                        valorCampos = item.Value.ToString();
                }                                           

            }
        }
        catch (Exception ex)
        {

        }


    }

1

Para obtener propiedades del documento dinámico cuando .GetType()regresa null, intente esto:

var keyValuePairs = ((System.Collections.Generic.IDictionary<string, object>)doc);
var val = keyValuePairs["propertyName"].ToObject<YourModel>;

0

En .Net core 3.1 puedes probar así

d?.value2 , d?.value3

0

Similar a la respuesta aceptada, también puede intentar en GetFieldlugar de GetProperty.

d.GetType().GetField("value2").GetValue(d);

Dependiendo de cómo Typese implementó el actual , esto puede funcionar cuando GetProperty () no lo hace e incluso puede ser más rápido.


Para su información, diferencia entre propiedad y campo en C # 3.0+: stackoverflow.com/a/653799/2680660
Efreeto
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.