Obter valor da propriedade dinâmica c # via string


182

Eu gostaria de acessar o valor de uma dynamicpropriedade c # com uma string:

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

Como posso obter o valor de d.value2 ("aleatório") se eu tenho apenas "value2" como uma string? Em javascript, eu poderia fazer d ["value2"] para acessar o valor ("random"), mas não sei como fazer isso com c # e reflexão. O mais perto que eu cheguei é isso:

d.GetType().GetProperty("value2") ... mas não sei como obter o valor real disso.

Como sempre, obrigado pela sua ajuda!


26
Observe que esse não é o objetivo pretendido de "dinâmico" e que esse cenário não funciona melhor com "dinâmico" do que com "objeto". "dinâmico" torna possível acessar propriedades quando o nome da propriedade é conhecido no momento da compilação, mas o tipo não. Como você não sabe o nome nem o tipo no momento da compilação, a dinâmica não ajudará.
Eric Lippert


3
@EricLippert Eu sei que esta pergunta é antiga, mas apenas para fazer um comentário, caso alguém a veja no futuro. Em alguns casos, você não pode optar por usar dinâmico ou objeto (por exemplo, ao usar o analisador JSON) e ainda pode querer obter as propriedades de uma sequência (de um arquivo de configuração, por exemplo), portanto, esse uso não é incomum como se pode pensar inicialmente.
Pedrom

Respostas:


217

Depois de obter seu PropertyInfo(de GetProperty), você precisa ligar GetValuee passar na instância em que deseja obter o valor. No seu caso:

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

4
Estou entrando 'd.GetType().GetProperty("value2").GetValue(d)' threw an exception of type 'System.Reflection.TargetInvocationException' dynamic {System.Reflection.TargetInvocationException}na vitrine com isso ..?
TimDog 8/02

6
Pense GetValue precisa de um parâmetro adicional -. EgdGetType () GetProperty ( "value2") GetValue (d, null).
Dommer

3
Isso funcionará em um ExpandoObject dinâmico verdadeiro, e não em um tipo anônimo? Como new {}cria um tipo anônimo real com propriedades definidas, chamar GetType / GetProperty faz sentido, mas o ExpandoObject, que se você chamar GetType, receberá um tipo que possui as propriedades de ExpandoObject, mas não necessariamente suas propriedades dinâmicas.
Triynko 27/01

16
-1. Isso funciona apenas com objetos .NET simples que foram convertidos em dinâmicos. Não vai funcionar com qualquer objeto dinâmico personalizado como Expando ou ViewBag usado ASP.NET MVC
Philipp Munin

8
isto é o que funciona com o Objeto Expando: (((IDictionary <string, objeto>) x)) ["value1"]
Michael Bahig

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);
}

Adicione referência ao Microsoft.CSharp. Funciona também para tipos dinâmicos e propriedades e campos particulares.

Edit : Enquanto essa abordagem funciona, existe um método quase 20 × mais rápido do assembly Microsoft.VisualBasic.dll :

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

2
Só queria mencionar que a versão VisualBasic não é equivalente à versão original 'GetProperty' (a GetProperty realmente chama o GetMember dinâmico, que funciona mesmo em objetos Python no IronPython).
Trevor Sundberg

qual seria o alvo do objeto?
Demodave

@Demodave O objeto no qual você deseja invocar a propriedade ( dna pergunta).
IllidanS4 quer Monica de volta

➕1 isso funcionou para propriedades privadas quando ambos FastMember e HyperDescriptor não o faria
Chris Marisic

@ IllidanS4 quando você comparou o CallSitecódigo vs CallByNamecódigo, você comparou os dois enquanto fazia o cache da CallSiteinstância? Eu suspeito que o custo do seu primeiro método é quase puramente a ativação do Bindere CallSite, não a invocaçãoTarget()
Chris Marisic

24

Dynamitey é uma .net stdbiblioteca de código-fonte aberto , que você pode chamá-la como a dynamicpalavra - chave, mas usando a string a para o nome da propriedade em vez do compilador fazendo isso por você, e acaba sendo igual à reflexão em velocidade (o que não é tão rápido como usar a palavra-chave dinâmica, mas isso ocorre devido à sobrecarga extra do armazenamento em cache dinamicamente, onde o compilador armazena em cache estaticamente).

Dynamic.InvokeGet(d,"value2");

11

O método mais fácil para obter a settere a getterpara uma propriedade que funcione para qualquer tipo, incluindo dynamice ExpandoObjecté usar, FastMemberque também é o método mais rápido (usa Emit).

Você pode obter uma TypeAccessorbaseada em um determinado tipo ou ObjectAccessoruma instância de um determinado tipo.

Exemplo:

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

Na maioria das vezes, quando você solicita um objeto dinâmico, obtém um ExpandoObject (não no exemplo anônimo, mas estático, da pergunta acima, mas menciona JavaScript e meu analisador JSON JsonFx escolhido, por exemplo, gera ExpandoObjects).

Se sua dinâmica é de fato um ExpandoObject, você pode evitar a reflexão convertendo-a no IDictionary, conforme descrito em http://msdn.microsoft.com/en-gb/library/system.dynamic.expandoobject.aspx .

Depois de converter para o IDictionary, você terá acesso a métodos úteis como .Item e .ContainsKey


Infelizmente, ter que converter para IDictionary e usar TryGetValue, por exemplo, resulta em um objeto antigo simples sendo retornado. Você não pode tirar proveito dos operadores implícitos nesse ponto, pois eles são considerados apenas em tempo de compilação. Por exemplo, se eu tivesse uma classe Int64Proxy com conversão implícita em Int64?, Procuraria Int64? i = data.value; //data is ExpandoObjectautomaticamente e chamaria o operador implícito. Por outro lado, se eu tivesse que usar o IDictionary para testar se o campo "valor" existe, eu recuperaria um objeto que não será convertido sem erro no Int64 ?.
Triynko 27/01

5

O GetProperty / GetValue não funciona para dados Json, sempre gera uma exceção nula; no entanto, você pode tentar esta abordagem:

Serialize seu objeto usando JsonConvert:

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

Em seguida, acesse-o diretamente, lançando-o de volta à string:

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

Pode funcionar diretamente aplicando o Convert.ToString (request) ["DynamicFieldName"], porém não testei.


2
Este método gera o erro: erro CS0021: Não é possível aplicar a indexação com [] a uma expressão do tipo 'objeto'. Use new JavaScriptSerializer().Deserialize<object>(json);para chegar às "propriedades" da maneira que você sugeriu
Kris Kilton

4

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

retorna um objeto PropertyInfo.

Então faça

propertyInfo.GetValue(d)

2
obrigado, esta foi a resposta correta, mas, como mencionado acima, as GetValue(d)necessidades devem serGetValue(d,null)
TimDog 08/02/11

4

Esta é a maneira como obtive o valor de um valor de propriedade de um dinâmico:

    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 obter propriedades do documento dinâmico quando .GetType()retornar null, tente o seguinte:

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

0

No .Net core 3.1, você pode tentar assim

d?.value2 , d?.value3

0

Semelhante à resposta aceita, você também pode tentar em GetFieldvez de GetProperty.

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

Dependendo de como o real Typefoi implementado, isso pode funcionar quando GetProperty () não e pode ser ainda mais rápido.


Diferença de
EFY
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.