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


188

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

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

在C#中,如何使用带有ExpandoObject和的dynamic关键字而又不会引发异常的方式做到这一点?


5
@CodeInChaos:请注意,显示的代码不会检查data.myProperty; 的值;它检查什么typeof data.myProperty回报。data.myProperty可能存在并设置为是正确的undefined,但在这种情况下,typeof将返回以外的值"undefined"。因此,此代码确实有效。
Aasmund Eldhuset 2012年

Answers:


181

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

3
为了简化此检查,我重载了TryGetValue并使其始终返回true,如果未定义该属性,则将返回值设置为“ undefined”。if(someObject.someParam!=“ undefined”)...它起作用:)
Softlion 2010年

也可能:),但是我认为您的意思是“覆盖”而不是重载。
Dykam 2010年

是的 我再次将“未定义”更改为在其他地方创建的特殊对象的const值。它可以防止铸造问题:p
Softlion 2010年

1
我相信这种解决方案仍然是最新的。不要以任何人为反思的代价说话-自己测试一下,看看您是否负担得起
nik.shornikov

1
@ BlueRaja-DannyPflughoeft是的,是的。如果不进行强制类型转换,那么这将是一个动态调用,并且要注意内部情况。更具体地说,它是明确实现的:github.com/mono/mono/blob/master/mcs/class/dlr/Runtime/…–
Dykam

28

这里需要进行重要的区分。

这里的大多数答案都是特定于问题中提到的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;
}
. . .

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-但这里是一个动态对象(class DDict),不会在Github上对未声明的属性进行null检查:

https://github.com/b9chris/GracefulDynamicDictionary

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


您为什么不赞成DynamicDictionary,因为它当时不使用反射?
Softlion

那么您可以投票赞成,因为这是相同的解决方案:)
Softlion 2014年

3
最肯定的是不一样的解决方案。
克里斯·莫斯基尼

“您将需要创建一个类似的子类,当找不到属性时,该子类不会抛出该子类。” =>是!哦,不,不是。我的解决方案更好。它会抛出-因为我们想要它,并且如果使用TryXX也不会抛出该异常;
Softlion 2014年

1
这正是我在这里的原因,我无法弄清楚为什么某些代码(视图包)没有中断。谢谢。
亚当·托利

11

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

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


11

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

2
@marklam在您不知道导致异常的原因时捕获所有异常是很不好的。在我们的情况下,这是可以的,因为我们期望可能没有字段。
亚历山大G

3
如果您知道导致异常的原因,那么您还必须知道其类型,因此请捕获(WhateverException)否则,即使您遇到意外的异常(例如,例如OutOfMemoryException),代码也将以静默方式继续运行。
marklam

4
您可以将任何getter传递给IsPropertyExist。在此示例中,您知道可以抛出InvalidOperationException。实际上,您不知道会引发什么异常。+1以抵消货物崇拜。
piedar 2013年

2
如果性能很重要,则此解决方案是不可接受的,例如,如果在具有500次以上迭代的循环中使用,则该解决方案加起来会导致很多秒的延迟。每次捕获到异常时,都必须将堆栈复制到异常对象
Jim109,2017年

1
关于:性能:附加的调试器和Console.WriteLine是慢速位。10,000次迭代耗时不到200毫秒(每次迭代有2个例外)。相同的测试无一例外需要花费几毫秒的时间。这意味着,如果您希望使用此代码的情况很少会丢失某个属性,或者您调用它的次数有限,或者可以缓存结果,那么请意识到一切都有它的位置,而且不要过多-在这里反驳的警告很重要。
混乱

10

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

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

4
对有用的评论投赞成票,并为ExpandoObject的扩展链接。
罗伯托

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
 } 

我不确定是否会返回动态添加的属性,我猜是它会返回Dynamic对象的方法。
Dykam 2010年

1

此扩展方法检查属性的存在,然后返回值或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");

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


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

4
这显示了如何实现动态对象,而不是如何看到动态对象上存在的属性。
马特·沃伦

您可以通过对相关属性进行空检查来检查动态实例是否具有属性。
ctorx 2011年

2
“这显示了如何实现动态对象”:是的,实际上是这样。这个问题的解决方案是:没有通用的解决方案,因为它取决于实现。
Softlion 2011年

@Softlion不,解决方案是我们必须停止使用的东西
nik.shornikov 2013年

@Softlion Tryxxx方法的意义是什么?当TryGet找不到属性时,它永远不会返回false,因此您仍然必须检查结果。回报是无用的。在TrySet中,如果键不存在,则它将引发异常而不是返回false。我不明白为什么您甚至会用它作为答案,如果您自己在此处写评论“此问题的解决方案是:没有通用的解决方案,因为它取决于实现”,这也不是事实。看看Dykam给出的真正解决方案的答案。
pqsk 2014年

-5

试试这个

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

3
这将检查隐藏在动态名称下的对象属性的存在,该名称是实现细节。发布之前,您是否已通过真实代码验证了您的解决方案?它根本不起作用。
Softlion

我在实时场景中使用了这段代码。效果很好。
Venkat 2013年

6
即使该属性存在,也一直使我为null。
亚特兰蒂斯

它适用于基本对象,但不适用于ExpandoObjects。动态,不确定。

1
要确认,这也不适用于dynamic对象(始终返回null)。
Gone Coding
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.