Respostas:
De acordo com o MSDN, a declaração mostra que está implementando o IDictionary:
public sealed class ExpandoObject : IDynamicMetaObjectProvider,
IDictionary<string, Object>, ICollection<KeyValuePair<string, Object>>,
IEnumerable<KeyValuePair<string, Object>>, IEnumerable, INotifyPropertyChanged
Você pode usar isso para ver se um membro está definido:
var expandoObject = ...;
if(((IDictionary<String, object>)expandoObject).ContainsKey("SomeMember")) {
// expandoObject.SomeMember exists.
}
Uma distinção importante precisa ser feita aqui.
A maioria das respostas aqui são específicas para o ExpandoObject, mencionado na pergunta. Mas um uso comum (e um motivo para abordar essa questão ao pesquisar) é ao usar o ASP.Net MVC ViewBag. Essa é uma implementação / subclasse personalizada do DynamicObject, que não gera uma exceção quando você verifica nulo em qualquer nome de propriedade arbitrária. Suponha que você possa declarar uma propriedade como:
@{
ViewBag.EnableThinger = true;
}
Suponha que você queira verificar seu valor e se está definido - se ele existe. O seguinte é válido, será compilado, não emitirá nenhuma exceção e fornece a resposta certa:
if (ViewBag.EnableThinger != null && ViewBag.EnableThinger)
{
// Do some stuff when EnableThinger is true
}
Agora livre-se da declaração de EnableThinger. O mesmo código compila e executa corretamente. Não há necessidade de reflexão.
Ao contrário do ViewBag, o ExpandoObject será lançado se você verificar nulo em uma propriedade que não existe. Para tirar a funcionalidade mais suave do MVC ViewBag de seus dynamic
objetos, você precisará usar uma implementação de dinâmica que não seja lançada.
Você pode simplesmente usar a implementação exata no MVC ViewBag:
. . .
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = ViewData[binder.Name];
// since ViewDataDictionary always returns a result even if the key does not exist, always return true
return true;
}
. . .
Você pode vê-lo vinculado às visualizações do MVC aqui, no MVC ViewPage:
http://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Mvc/ViewPage.cs
A chave do comportamento gracioso do DynamicViewDataDictionary é a implementação do Dictionary no ViewDataDictionary, aqui:
public object this[string key]
{
get
{
object value;
_innerDictionary.TryGetValue(key, out value);
return value;
}
set { _innerDictionary[key] = value; }
}
https://github.com/ASP-NET-MVC/aspnetwebstack/blob/master/src/System.Web.Mvc/ViewDataDictionary.cs
Em outras palavras, ele sempre retorna um valor para todas as chaves, independentemente do que está nele - ele simplesmente retorna nulo quando não existe nada. Porém, o ViewDataDictionary tem o ônus de estar vinculado ao modelo do MVC, portanto, é melhor remover apenas as partes graciosas do dicionário para uso fora das visualizações do MVC.
É muito longo para realmente postar todos os detalhes aqui - a maioria implementando apenas o IDictionary - mas aqui está um objeto dinâmico (classe DDict
) que não gera verificações nulas nas propriedades que não foram declaradas no Github:
https://github.com/b9chris/GracefulDynamicDictionary
Se você quiser apenas adicioná-lo ao seu projeto via NuGet, o nome dele é GracefulDynamicDictionary .
Respondi a uma pergunta muito semelhante recentemente: como reflito sobre os membros do objeto dinâmico?
Em breve, ExpandoObject não é o único objeto dinâmico que você pode obter. O Reflection funcionaria para tipos estáticos (tipos que não implementam IDynamicMetaObjectProvider). Para tipos que implementam essa interface, a reflexão é basicamente inútil. Para ExpandoObject, você pode simplesmente verificar se a propriedade está definida como uma chave no dicionário subjacente. Para outras implementações, pode ser desafiador e, às vezes, a única maneira é trabalhar com exceções. Para detalhes, siga o link acima.
ATUALIZADO: você pode usar delegados e tentar obter um valor da propriedade do objeto dinâmico, se existir. Se não houver propriedade, simplesmente pegue a exceção e retorne false.
Dê uma olhada, ele funciona bem para mim:
class Program
{
static void Main(string[] args)
{
dynamic userDynamic = new JsonUser();
Console.WriteLine(IsPropertyExist(() => userDynamic.first_name));
Console.WriteLine(IsPropertyExist(() => userDynamic.address));
Console.WriteLine(IsPropertyExist(() => userDynamic.last_name));
}
class JsonUser
{
public string first_name { get; set; }
public string address
{
get
{
throw new InvalidOperationException("Cannot read property value");
}
}
}
static bool IsPropertyExist(GetValueDelegate getValueMethod)
{
try
{
//we're not interesting in the return value. What we need to know is whether an exception occurred or not
getValueMethod();
return true;
}
catch (RuntimeBinderException)
{
// RuntimeBinderException occurred during accessing the property
// and it means there is no such property
return false;
}
catch
{
//property exists, but an exception occurred during getting of a value
return true;
}
}
delegate string GetValueDelegate();
}
A saída do código é a seguinte:
True
True
False
IsPropertyExist
. Neste exemplo, você sabe que é possível lançar um InvalidOperationException
. Na prática, você não tem idéia de que exceção pode ser lançada. +1 para neutralizar o culto à carga.
Eu queria criar um método de extensão para poder fazer algo como:
dynamic myDynamicObject;
myDynamicObject.propertyName = "value";
if (myDynamicObject.HasProperty("propertyName"))
{
//...
}
... mas você não pode criar extensões de ExpandoObject
acordo com a documentação do C # 5 (mais informações aqui ).
Então acabei criando um auxiliar de classe:
public static class ExpandoObjectHelper
{
public static bool HasProperty(ExpandoObject obj, string propertyName)
{
return ((IDictionary<String, object>)obj).ContainsKey(propertyName);
}
}
Para usá-lo:
// If the 'MyProperty' property exists...
if (ExpandoObjectHelper.HasProperty(obj, "MyProperty"))
{
...
}
Por que você não deseja usar o Reflection para obter um conjunto de tipos properyes? Como isso
dynamic v = new Foo();
Type t = v.GetType();
System.Reflection.PropertyInfo[] pInfo = t.GetProperties();
if (Array.Find<System.Reflection.PropertyInfo>(pInfo, p => { return p.Name == "PropName"; }). GetValue(v, null) != null))
{
//PropName initialized
}
Esse método de extensão verifica a existência de uma propriedade e, em seguida, retorna o valor ou nulo. Isso é útil se você não deseja que seus aplicativos gerem exceções desnecessárias, pelo menos as que você pode ajudar.
public static object Value(this ExpandoObject expando, string name)
{
var expandoDic = (IDictionary<string, object>)expando;
return expandoDic.ContainsKey(name) ? expandoDic[name] : null;
}
Se pode ser usado como tal:
// lookup is type 'ExpandoObject'
object value = lookup.Value("MyProperty");
ou se sua variável local for 'dinâmica', será necessário convertê-la no ExpandoObject primeiro.
// lookup is type 'dynamic'
object value = ((ExpandoObject)lookup).Value("PropertyBeingTested");
Dependendo do seu caso de uso, se nulo puder ser considerado o mesmo que indefinido, você poderá transformar seu ExpandoObject em um DynamicJsonObject.
dynamic x = new System.Web.Helpers.DynamicJsonObject(new ExpandoObject());
x.a = 1;
x.b = 2.50;
Console.WriteLine("a is " + (x.a ?? "undefined"));
Console.WriteLine("b is " + (x.b ?? "undefined"));
Console.WriteLine("c is " + (x.c ?? "undefined"));
Resultado:
a is 1
b is 2.5
c is undefined
(authorDynamic as ExpandoObject).Any(pair => pair.Key == "YourProp");
Ei, parem de usar o Reflection para tudo o que custa muitos ciclos de CPU.
Aqui está a solução:
public class DynamicDictionary : DynamicObject
{
Dictionary<string, object> dictionary = new Dictionary<string, object>();
public int Count
{
get
{
return dictionary.Count;
}
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
string name = binder.Name;
if (!dictionary.TryGetValue(binder.Name, out result))
result = "undefined";
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
dictionary[binder.Name] = value;
return true;
}
}
Tente este
public bool PropertyExist(object obj, string propertyName)
{
return obj.GetType().GetProperty(propertyName) != null;
}
dynamic
objetos (sempre retorna null
).
data.myProperty
; verifica o quetypeof data.myProperty
retorna. É correto quedata.myProperty
possa existir e estar definido comoundefined
, mas nesse caso,typeof
retornará algo diferente"undefined"
. Portanto, esse código funciona.