如何检测ExpandoObject上是否存在属性?

在JavaScript中,您可以使用undefined关键字来检测属性是否被定义:

if( typeof data.myProperty == "undefined" ) ... 

你将如何在C#中使用dynamic关键字与ExpandoObject做到这一点,而不会引发exception?

根据MSDN声明显示它正在实现IDictionary:

 public sealed class ExpandoObject : IDynamicMetaObjectProvider, IDictionary<string, Object>, ICollection<KeyValuePair<string, Object>>, IEnumerable<KeyValuePair<string, Object>>, IEnumerable, INotifyPropertyChanged 

您可以使用这个来查看是否定义了一个成员:

 var expandoObject = ...; if(((IDictionary<String, object>)expandoObject).ContainsKey("SomeMember")) { // expandoObject.SomeMember exists. } 

这里需要做一个重要的区分。

这里的大部分答案都是特定于问题中提到的ExpandoObject。 但是一个常见的用法(以及在search时着陆在这个问题上的理由)是使用ASP.Net MVC ViewBag。 这是DynamicObject的一个自定义实现/子类,当您检查任何任意属性名称为null时,不会抛出exception。 假设你可能声明一个属性如下:

 @{ ViewBag.EnableThinger = true; } 

那么,假设你想检查它的价值,甚至是否设置 – 是否存在。 以下是有效的,将编译,不会抛出任何exception,并给你正确的答案:

 if (ViewBag.EnableThinger != null && ViewBag.EnableThinger) { // Do some stuff when EnableThinger is true } 

现在摆脱EnableThinger的声明。 相同的代码编译并正确运行。 不需要反思。

与ViewBag不同,如果您在不存在的属性上检查null,则会抛出ExpandoObject。 为了从dynamic对象中获得MVC ViewBag的更温和的function,您需要使用不会抛出的dynamic实现。

您可以简单地在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; } . . . 

https://github.com/ASP-NET-MVC/aspnetwebstack/blob/master/src/System.Web.Mvc/DynamicViewDataDictionary.cs

您可以在MVC ViewPage中看到它被绑定到MVC Views中:

http://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Mvc/ViewPage.cs

DynamicViewDataDictionary的优雅行为的关键是ViewDataDictionary上的Dictionary实现,在这里:

 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

换句话说,它总是为所有的键返回一个值,而不pipe它是什么 – 当没有任何东西的时候,它只是返回null。 但是,ViewDataDictionary有负担绑定到MVC的模型,所以最好是去掉MVC Views以外的优雅的字典部分。

在这里真的发布所有的胆量都太长了 – 大部分只是实现了IDictionary – 但是这里是一个dynamic的对象,它不会对Github上没有声明的属性进行空值检查:

https://github.com/b9chris/GracefulDynamicDictionary

如果您只是想通过NuGet将其添加到您的项目,它的名字是GracefulDynamicDictionary 。

我最近回答了一个非常类似的问题: 我如何反映dynamic对象的成员?

不久,ExpandoObject不是您可能获得的唯一dynamic对象。 reflection将适用于静态types(不实现IDynamicMetaObjectProvider的types)。 对于实现这个接口的types,reflection基本上是无用的。 对于ExpandoObject,您可以简单地检查该属性是否被定义为底层字典中的键。 对于其他实现,这可能是具有挑战性的,有时唯一的办法是处理exception。 有关详细信息,请按照上面的链接。

更新:您可以使用委托,并尝试从dynamic对象属性,如果它存在的值。 如果没有属性,只需捕获exception并返回false。

看一下,它对我来说工作正常:

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

代码的输出如下:

 True True False 

我想创build一个扩展方法,所以我可以做这样的事情:

 dynamic myDynamicObject; myDynamicObject.propertyName = "value"; if (myDynamicObject.HasProperty("propertyName")) { //... } 

…但是您不能根据C#5文档(详细信息,请参阅此处 )在ExpandoObject上创build扩展。

所以我最终创build了一个类助手:

 public static class ExpandoObjectHelper { public static bool HasProperty(ExpandoObject obj, string propertyName) { return ((IDictionary<String, object>)obj).ContainsKey(propertyName); } } 

为什么你不想使用reflection来获得一组typesproperyes? 喜欢这个

  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 } 

这个扩展方法检查一个属性的存在,然后返回值或null。 如果你不希望你的应用程序抛出不必要的exception,那么这是很有用的,至less你可以提供帮助。

  public static object Value(this ExpandoObject expando, string name) { var expandoDic = (IDictionary<string, object>)expando; return expandoDic.ContainsKey(name) ? expandoDic[name] : null; } 

如果可以这样使用:

  // lookup is type 'ExpandoObject' object value = lookup.Value("MyProperty"); 

或者如果你的本地variables是“dynamic的”,你将不得不先把它转换成ExpandoObject。

  // lookup is type 'dynamic' object value = ((ExpandoObject)lookup).Value("PropertyBeingTested"); 
 (authorDynamic as ExpandoObject).Any(pair => pair.Key == "YourProp"); 

嘿,大家停止使用reflection一切花费大量的CPU周期。

这是解决scheme:

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

试试这个

 public bool PropertyExist(object obj, string propertyName) { return obj.GetType().GetProperty(propertyName) != null; }