How to detect if a property exists on an ExpandoObject?

橙三吉。 提交于 2019-12-27 11:36:55

问题


In javascript you can detect if a property is defined by using the undefined keyword:

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

How would you do this in C# using the dynamic keyword with an ExpandoObject and without throwing an exception?


回答1:


According to MSDN the declaration shows it is implementing IDictionary:

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

You can use this to see if a member is defined:

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



回答2:


An important distinction needs to be made here.

Most of the answers here are specific to the ExpandoObject which is mentioned in the question. But a common usage (and reason to land on this question when searching) is when using the ASP.Net MVC ViewBag. That's a custom implementation/subclass of DynamicObject, which won't throw an Exception when you check any arbitrary property name for null. Suppose you might declare a property like:

@{
    ViewBag.EnableThinger = true;
}

Then suppose you wanted to check its value, and whether it's even set - whether it exists. The following is valid, will compile, won't throw any exceptions, and gives you the right answer:

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

Now get rid of the declaration of EnableThinger. Same code compiles and runs properly. No need for reflection.

Unlike ViewBag, ExpandoObject will throw if you check for null on a property that doesn't exist. In order to get MVC ViewBag's gentler functionality out of your dynamic objects, you'll need to use an implementation of dynamic that doesn't throw.

You could simply use the exact implementation in 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

You can see it being tied into MVC Views here, in MVC ViewPage:

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

The key to DynamicViewDataDictionary's graceful behavior is the Dictionary implementation on ViewDataDictionary, here:

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

In other words, it always returns a value for all keys, regardless of what's in it - it simply returns null when nothing's there. But, ViewDataDictionary has the burden of being tied to MVC's Model, so it's better to strip out just the graceful dictionary parts for use outside MVC Views.

It's too long to really post all the guts here - most of it just implementing IDictionary - but here's a dynamic object (class DDict) that doesn't throw for null checks on properties that haven't been declared, on Github:

https://github.com/b9chris/GracefulDynamicDictionary

If you just want to add it to your project via NuGet, its name is GracefulDynamicDictionary.




回答3:


I answered a very similar question recently: How do I reflect over the members of dynamic object?

Shortly, ExpandoObject is not the only dynamic object you might get. Reflection would work for static types (types that do not implement IDynamicMetaObjectProvider). For types that do implement this interface, reflection is basically useless. For ExpandoObject, you can simply check whether the property is defined as a key in the underlying dictionary. For other implementations, it might be challenging and sometimes the only way is to work with exceptions. For details, follow the link above.




回答4:


UPDATED: You can use delegates and try to get a value from the dynamic object property if it exists. If there is no property, simply catch the exception and return false.

Take a look, it works fine for me:

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

The output of the code is the following:

True
True
False



回答5:


I wanted to create an extension method so I could do something like:

dynamic myDynamicObject;
myDynamicObject.propertyName = "value";

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

... but you can't create extensions on ExpandoObject according to the C# 5 documentation (more info here).

So I ended up creating a class helper:

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

To use it:

// If the 'MyProperty' property exists...
if (ExpandoObjectHelper.HasProperty(obj, "MyProperty"))
{
    ...
}



回答6:


Why you do not want to use Reflection to get set of type properyes? Like this

 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
 } 



回答7:


This extension method checks for the existence of a property and then returns the value or null. This is useful if you do not want your applications to throw unnecessary exceptions, at least ones you can help.

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

If can be used as such :

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

or if your local variable is 'dynamic' you will have to cast it to ExpandoObject first.

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



回答8:


Depending on your use case, if null can be considered as being the same as undefined, you can turn your ExpandoObject into a 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"));

Output:

a is 1
b is 2.5
c is undefined



回答9:


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



回答10:


Hey guys stop using Reflection for everything it costs a lots of CPU cycles.

Here is the solution:

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



回答11:


Try this one

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


来源:https://stackoverflow.com/questions/2839598/how-to-detect-if-a-property-exists-on-an-expandoobject

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!