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!

TimDog
fonte
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
Possivelmente relacionado: stackoverflow.com/questions/5877251/… .
DuckMaestro
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);
Adam Robinson
fonte
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);
}
IllidanS4 quer Monica de volta
fonte
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");
jbtule
fonte
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");
MaYaN
fonte
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

Francis Norton
fonte
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.

Anderson
fonte
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)
James Gaunt
fonte
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)
        {

        }


    }
Marcelo Lima Braga
fonte
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>;
yzhai bruin
fonte
0

No .Net core 3.1, você pode tentar assim

d?.value2 , d?.value3
vyeluri5
fonte
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.

Efreeto
fonte
Diferença de
EFY