Как определить, существует ли свойство в ExpandoObject?

В javascript вы можете определить, определено ли свойство с помощью ключевого слова undefined:

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

Как бы вы это сделали в С#, используя динамическое ключевое слово с ExpandoObject и не выбрасывая исключение?

Ответ 1

В соответствии с 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.
}

Ответ 2

Здесь необходимо сделать важное различие.

Большинство ответов здесь относятся к 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, ExpandoObject будет выдавать, если вы проверите null для свойства, которого не существует. Чтобы получить более привлекательную функциональность 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;
}
. . .

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

Вы можете видеть, что он связан с MVC Views здесь, в MVC ViewPage:

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

Ключ к изящному поведению DynamicViewDataDictionary - реализация словаря в ViewDataDictionary, здесь:

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 Model, поэтому лучше выделять только изящные части словаря для использования вне MVC Views.

Слишком долго, чтобы действительно опубликовать все хитрости здесь - большинство из них просто реализует IDictionary, но здесь динамический объект, который не выбрасывает нулевые проверки свойств, которые не были объявлены, в Github:

https://github.com/b9chris/GracefulDynamicDictionary

Если вы просто хотите добавить его в свой проект через NuGet, его имя GracefulDynamicDictionary.

Ответ 3

Недавно я ответил на очень похожий вопрос: Как мне отразить элементы динамического объекта?

Вскоре ExpandoObject не является единственным динамическим объектом, который вы можете получить. Отражение будет работать для статических типов (типов, которые не реализуют IDynamicMetaObjectProvider). Для типов, реализующих этот интерфейс, отражение в основном бесполезно. Для ExpandoObject вы можете просто проверить, определено ли свойство как ключ в базовом словаре. Для других реализаций это может быть сложной задачей, и иногда единственный способ - работать с исключениями. Подробнее см. Ссылку выше.

Ответ 4

ОБНОВЛЕНО: вы можете использовать делегаты и попытаться получить значение из свойства динамического объекта, если оно существует. Если нет свойства, просто поймайте исключение и верните 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

Ответ 5

Я хотел создать метод расширения, чтобы я мог сделать что-то вроде:

dynamic myDynamicObject;
myDynamicObject.propertyName = "value";

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

... но вы не можете создавать расширения в ExpandoObject в соответствии с документацией С# 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"))
{
    ...
}

Ответ 6

Почему вы не хотите использовать 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
 } 

Ответ 7

Этот метод расширения проверяет наличие свойства и затем возвращает значение или 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");

Ответ 8

В зависимости от вашего варианта использования, если null может рассматриваться как совпадающий с undefined, вы можете превратить ваш 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

Ответ 9

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

Ответ 10

Эй, ребята, перестали использовать Reflection для всего, что стоило много циклов процессора.

Вот решение:

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

Попробуйте этот

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