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

Sof*_*ion 182 c# dynamic expandoobject

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

if( typeof data.myProperty == "undefined" ) ...
Run Code Online (Sandbox Code Playgroud)

你如何在C#中使用带有ExpandoObject和不带异常的动态关键字来做到这一点?

Dyk*_*kam 177

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

public sealed class ExpandoObject : IDynamicMetaObjectProvider, 
    IDictionary<string, Object>, ICollection<KeyValuePair<string, Object>>, 
    IEnumerable<KeyValuePair<string, Object>>, IEnumerable, INotifyPropertyChanged
Run Code Online (Sandbox Code Playgroud)

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

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

  • 为了使这个检查更简单,我重载了TryGetValue并使其始终返回true,如果未定义属性,则将返回值设置为"undefined".if(someObject.someParam!="undefined")......它的工作原理:) (3认同)

Chr*_*ini 28

需要在这里做出重要的区分.

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

@{
    ViewBag.EnableThinger = true;
}
Run Code Online (Sandbox Code Playgroud)

然后假设您想检查它的值,以及它是否设置 - 是否存在.以下是有效的,将编译,不会抛出任何异常,并给你正确的答案:

if (ViewBag.EnableThinger != null && ViewBag.EnableThinger)
{
    // Do some stuff when EnableThinger is true
}
Run Code Online (Sandbox Code Playgroud)

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

与ViewBag不同,如果在不存在的属性上检查null,ExpandoObject将抛出.为了从对象中获取MVC ViewBag的温和功能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;
}
. . .
Run Code Online (Sandbox Code Playgroud)

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; }
}
Run Code Online (Sandbox Code Playgroud)

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.

  • 它肯定不是同一个解决方案. (3认同)
  • 这,这正是我在这里的原因,我无法弄清楚为什么某些代码(viewbag)没有被破坏。谢谢你。 (2认同)

Ale*_*ina 11

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

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


Ale*_*r G 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();
}
Run Code Online (Sandbox Code Playgroud)

代码的输出如下:

True
True
False
Run Code Online (Sandbox Code Playgroud)

  • 你可以将任何getter传递给`IsPropertyExist`.在这个例子中,你知道可以抛出一个`InvalidOperationException`.在实践中,您不知道可能抛出什么异常.+1抵消货运崇拜. (4认同)
  • @marklam在您不知道导致异常的原因时捕获所有异常是很不好的。在我们的情况下,这是可以的,因为我们期望可能没有字段。 (2认同)
  • 如果你知道导致异常的原因你也必须知道它的类型,所以catch(WhateverException)否则你的代码将默默地继续进行,即使你遇到了意外的异常 - 比如OutOfMemoryException. (2认同)
  • 如果性能很重要,则此解决方案是不可接受的,例如,如果在具有 500 多次迭代的循环中使用,它会累加并导致许多秒的延迟。每次捕获异常时,必须将堆栈复制到异常对象 (2认同)

Max*_*ime 9

我想创建一个扩展方法,所以我可以这样做:

dynamic myDynamicObject;
myDynamicObject.propertyName = "value";

if (myDynamicObject.HasProperty("propertyName"))
{
    //...
}
Run Code Online (Sandbox Code Playgroud)

...但您无法ExpandoObject根据C#5文档创建扩展(此处有更多信息).

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

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

要使用它:

// If the 'MyProperty' property exists...
if (ExpandoObjectHelper.HasProperty(obj, "MyProperty"))
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

  • 对有用的评论投赞成票,并为ExpandoObject的扩展链接。 (3认同)