Como eu reflito sobre os membros do objeto dinâmico?

131

Preciso obter um dicionário de propriedades e seus valores de um objeto declarado com a palavra-chave dinâmica no .NET 4? Parece que usar a reflexão para isso não funcionará.

Exemplo:

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
fonte

Respostas:

32

Se o IDynamicMetaObjectProvider puder fornecer os nomes de membros dinâmicos, você poderá obtê-los. Veja a implementação de GetMemberNames na biblioteca PCL licenciada do apache Dynamitey (que pode ser encontrada em nuget), ela funciona para ExpandoObjects e DynamicObjects que implementam GetDynamicMemberNamese para qualquer outro IDynamicMetaObjectProviderque fornece um metaobjeto com uma implementação GetDynamicMemberNamessem testes personalizados além is IDynamicMetaObjectProvider.

Depois de obter os nomes dos membros, é um pouco mais trabalhoso obter o valor da maneira certa, mas o Impromptu faz isso, mas é mais difícil apontar apenas os bits interessantes e fazer com que faça sentido. Aqui está a documentação e é igual ou mais rápida que a reflexão; no entanto, é improvável que seja mais rápida que uma pesquisa no dicionário para expando, mas funciona para qualquer objeto, expando, dinâmico ou original - você escolhe.

jbtule
fonte
17
Graças a esse ponto, o código é: var tTarget = target as IDynamicMetaObjectProvider; if (tTarget! = null) {tList.AddRange (tTarget.GetMetaObject (Expression.Constant (tTarget)). GetDynamicMemberNames ()); }
Flatliner DOA
obrigado pela sua ótima biblioteca. Eu estava com problemas para fazer um round-trip de um objeto dinâmico para esta biblioteca dynamicjson.codeplex.com e pude estendê -lo com sua biblioteca.
JJS 30/07
1
exemplo por favor.
Demodave
105

No caso de ExpandoObject, a classe ExpandoObject realmente implementa IDictionary<string, object>suas propriedades, portanto a solução é tão trivial quanto a conversão:

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

Observe que isso não funcionará para objetos dinâmicos gerais. Nesses casos, você precisará ir para o DLR via IDynamicMetaObjectProvider.

Itowlson
fonte
Obrigado por isso, infelizmente a amostra foi um pouco simplificada. Eu preciso ser capaz de inspecionar um objeto dinâmico sem saber qual é o seu tipo real.
Flatliner DOA
2
Isso funciona apenas para objetos da classe ExpandoObject, a classe DynamicObject é outra classe extensível que não implementa o IDictionary, mas implementa o IDynamicMetaObjectProvider.
Sharique Abdullah
1
Porém, ele responde à pergunta do OP.
Sharique Abdullah
58

Existem vários cenários a serem considerados. Primeiro de tudo, você precisa verificar o tipo do seu objeto. Você pode simplesmente chamar GetType () para isso. Se o tipo não implementar IDynamicMetaObjectProvider, você poderá usar a mesma reflexão de qualquer outro objeto. Algo como:

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

No entanto, para implementações IDynamicMetaObjectProvider, a reflexão simples não funciona. Basicamente, você precisa saber mais sobre esse objeto. Se for ExpandoObject (que é uma das implementações IDynamicMetaObjectProvider), você poderá usar a resposta fornecida por itowlson. ExpandoObject armazena suas propriedades em um dicionário e você pode simplesmente converter seu objeto dinâmico em um dicionário.

Se for DynamicObject (outra implementação de IDynamicMetaObjectProvider), será necessário usar quaisquer métodos que este DynamicObject exponha. O DynamicObject não é necessário para "armazenar" realmente sua lista de propriedades em qualquer lugar. Por exemplo, pode ser algo como isto (estou reutilizando um exemplo da minha postagem no blog ):

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

Nesse caso, sempre que você tentar acessar uma propriedade (com qualquer nome), o objeto simplesmente retornará o nome da propriedade como uma sequência.

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

Portanto, você não tem nada para refletir - esse objeto não possui propriedades e, ao mesmo tempo, todos os nomes de propriedades válidos funcionarão.

Eu diria que para implementações IDynamicMetaObjectProvider, você precisa filtrar implementações conhecidas, onde pode obter uma lista de propriedades, como ExpandoObject, e ignorar (ou lançar uma exceção) para o resto.

Alexandra Rusina
fonte
7
Parece-me que, se o tipo que eu consumir é Dinâmico, não devo fazer suposições sobre o tipo subjacente. Eu devo chamar GetDynamicMemberNames para obter a lista de membros. Parece estúpido que os tipos estáticos tenham melhor suporte à inspeção em tempo de execução do que os dinâmicos!
Flatliner DOA
2
Você pode substituir o método GetDynamicMemberNames () de um objeto dinâmico para retornar os nomes da lista de membros dinâmicos. O problema é que não é garantido que todo objeto dinâmico possua esse método (o ExpandoObject não possui). Não é de surpreender que a reflexão funcione melhor para tipos estáticos. Foi criado para eles em primeiro lugar. Com a dinâmica, você precisa confiar mais em testes de unidade e TDD.
Alexandra Rusina
1
A documentação do MSDN para GetDynamicMemberNames () menciona: "existe Este método somente para fins de depuração.", Não é realmente confortante :(
NicoJuicy
19

Requer Newtonsoft Json.Net

Um pouco tarde, mas eu vim com isso. Ele fornece apenas as chaves e você pode usá-las na 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;
}

Então você simplesmente projeta assim:

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

Optando por obter o valor como uma sequência ou outro objeto, ou faça outra dinâmica e use a pesquisa novamente.

Mr. B
fonte
Você não precisa da lista para retornar.
aloisdg movendo-se para codidact.com 16/11
4
Perfeito! Eu tive que mudar JObject attributeAsJObject = dynamicToGetPropertiesFor; para Object attributeAsJObject = (JObject) JToken.FromObject (obj); Apesar!!
K25
Apenas para reiterar o que o @ k25 disse. É necessário substituir JObject attributesAsJObject = dynamicToGetPropertiesFor;com algo como: var jObject = (JObject) JToken.FromObject(dynamicToGetPropertiesFor);. Nesse ponto, você pode obter um dicionário de nomes e valores de propriedades fazendo algo parecido var objProperties = jObject.ToObject<Dictionary<string, object>>();. Com isso em mãos, você está pronto para as corridas. Isso não precisa de uma dinâmica. Ele funciona bem com qualquer coisa que é uma subclasse deDynamicObject
Flydog57