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

时间:2010-05-15 09:23:56

标签: c# dynamic expandoobject

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

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

如何使用带有ExpandoObject的动态关键字在C#中执行此操作而不抛出异常?

11 个答案:

答案 0 :(得分:174)

根据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.
}

答案 1 :(得分:27)

需要在这里做出重要区分。

这里的大部分答案都是针对问题中提到的ExpandoObject特有的。但是在使用ASP.Net MVC ViewBag时,常见的用法(以及在搜索时找到此问题的理由)。这是DynamicObject的自定义实现/子类,当您检查任何属性名称为null时,它不会抛出异常。假设你可以声明一个属性:

@{
    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;
}
. . .

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

你可以在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 - 但是这里是一个动态对象,它不会对Github上尚未声明的属性进行空检查:

https://github.com/b9chris/GracefulDynamicDictionary

如果您只想通过NuGet将其添加到项目中,其名称为GracefulDynamicDictionary

答案 2 :(得分:11)

我最近回答了一个非常相似的问题:How do I reflect over the members of dynamic object?

很快,ExpandoObject不是您可能获得的唯一动态对象。反射适用于静态类型(不实现IDynamicMetaObjectProvider的类型)。对于实现此接口的类型,反射基本上是无用的。对于ExpandoObject,您只需检查属性是否定义为基础字典中的键。对于其他实现,它可能具有挑战性,有时唯一的方法是使用异常。有关详细信息,请点击上面的链接。

答案 3 :(得分:10)

更新:您可以使用委托并尝试从动态对象属性中获取值(如果存在)。如果没有属性,只需捕获异常并返回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

答案 4 :(得分:7)

我想创建扩展方法,以便我可以执行以下操作:

dynamic myDynamicObject;
myDynamicObject.propertyName = "value";

if (myDynamicObject.HasProperty("propertyName"))
{
    //...
}

...但您无法根据C#5文档在ExpandoObject上创建扩展程序(更多信息here)。

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

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"))
{
    ...
}

答案 5 :(得分:1)

为什么你不想使用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
 } 

答案 6 :(得分:1)

此扩展方法检查属性是否存在,然后返回值或null。如果您不希望应用程序抛出不必要的异常,这是非常有用的,至少可以提供帮助。

RequireUserPermission

如果可以这样使用:

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

或者如果您的局部变量是'动态',则必须先将其强制转换为ExpandoObject。

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

答案 7 :(得分:1)

根据您的用例,如果可以将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

答案 8 :(得分:-2)

(authorDynamic as ExpandoObject).Any(pair => pair.Key == "YourProp");

答案 9 :(得分:-3)

嘿伙计们停止使用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;
    }
}

答案 10 :(得分:-5)

试试这个

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