Answers:
根据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的。但是一种常见用法(以及在搜索时出现此问题的原因)是使用ASP.Net MVC ViewBag时。这是DynamicObject的自定义实现/子类,当您检查任何属性名称是否为null时,都不会引发Exception。假设您可以声明如下属性:
@{
ViewBag.EnableThinger = true;
}
然后假设您要检查它的值,以及它是否已设置-是否存在。以下内容有效,可以编译,不会引发任何异常,并为您提供正确的答案:
if (ViewBag.EnableThinger != null && ViewBag.EnableThinger)
{
// Do some stuff when EnableThinger is true
}
现在摆脱EnableThinger的声明。相同的代码可以编译并正常运行。无需反思。
与ViewBag不同,如果在不存在的属性中检查是否为null,ExpandoObject将引发。为了从您的dynamic
对象中获得MVC ViewBag的柔和功能,您需要使用不会抛出异常的动态实现。
您可以简单地在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;
}
. . .
您可以在此处的MVC ViewPage中看到将其绑定到MVC视图中:
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
换句话说,它总是为所有键返回一个值,而不管其中有什么-当没有键时,它只是返回null。但是,ViewDataDictionary具有与MVC的模型绑定的负担,因此最好只删除优美的字典部分以在MVC视图之外使用。
真正在这里发布所有内容的时间太长了-大多数都只是实现IDictionary-但这里是一个动态对象(class DDict
),不会在Github上对未声明的属性进行null检查:
https://github.com/b9chris/GracefulDynamicDictionary
如果只想通过NuGet将其添加到项目中,则其名称为GracefulDynamicDictionary。
我最近回答了一个非常相似的问题:如何反映动态对象的成员?
不久,ExpandoObject不是您可能获得的唯一动态对象。反射将适用于静态类型(不实现IDynamicMetaObjectProvider的类型)。对于确实实现此接口的类型,反射基本上是没有用的。对于ExpandoObject,您只需检查属性是否在基础字典中定义为键。对于其他实现,这可能具有挑战性,有时唯一的方法就是处理异常。有关详细信息,请单击上面的链接。
更新:您可以使用委托并尝试从动态对象属性获取值(如果存在)。如果没有属性,只需捕获异常并返回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
IsPropertyExist
。在此示例中,您知道可以抛出InvalidOperationException
。实际上,您不知道会引发什么异常。+1以抵消货物崇拜。
我想创建一个扩展方法,以便可以执行以下操作:
dynamic myDynamicObject;
myDynamicObject.propertyName = "value";
if (myDynamicObject.HasProperty("propertyName"))
{
//...
}
...但是您不能ExpandoObject
根据C#5文档(在此处获取更多信息)创建扩展。
因此,我最终创建了一个类帮助器:
public static class ExpandoObjectHelper
{
public static bool HasProperty(ExpandoObject obj, string propertyName)
{
return ((IDictionary<String, object>)obj).ContainsKey(propertyName);
}
}
要使用它:
// If the 'MyProperty' property exists...
if (ExpandoObjectHelper.HasProperty(obj, "MyProperty"))
{
...
}
为什么不想使用Reflection获得类型属性集?像这样
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。如果您不希望您的应用程序抛出不必要的异常(至少可以提供帮助),则这很有用。
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");
如果您的局部变量是“动态的”,则必须先将其强制转换为ExpandoObject。
// lookup is type 'dynamic'
object value = ((ExpandoObject)lookup).Value("PropertyBeingTested");
根据您的用例,如果可以将null视为未定义的值,则可以将ExpandoObject转换为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"));
输出:
a is 1
b is 2.5
c is undefined
大家停止使用Reflection来解决所有花费大量CPU周期的事情。
解决方法如下:
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;
}
dynamic
对象(始终返回null
)。
data.myProperty
; 的值;它检查什么typeof data.myProperty
回报。data.myProperty
可能存在并设置为是正确的undefined
,但在这种情况下,typeof
将返回以外的值"undefined"
。因此,此代码确实有效。