Как определить, существует ли свойство в ExpandoObject? - PullRequest
177 голосов
/ 15 мая 2010

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

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

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

Ответы [ 10 ]

174 голосов
/ 15 мая 2010

Согласно 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.
}
27 голосов
/ 12 июня 2014

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

Большинство ответов здесь относятся только к 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-представлениям здесь, в 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

Другими словами, он всегда возвращает значение для всех ключей, независимо от того, что в нем, - он просто возвращает ноль, когда там ничего нет. Но ViewDataDictionary имеет бремя привязки к модели MVC, поэтому лучше исключить только изящные части словаря для использования вне MVC Views.

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

https://github.com/b9chris/GracefulDynamicDictionary

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

11 голосов
/ 17 мая 2010

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

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

10 голосов
/ 19 сентября 2012

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

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

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"))
{
    ...
}
1 голос
/ 07 августа 2017

Этот метод расширения проверяет наличие свойства, а затем возвращает значение или ноль. Это полезно, если вы не хотите, чтобы ваши приложения выдавали ненужные исключения, по крайней мере, те, которые вы можете помочь.

    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 голос
/ 15 мая 2010

Почему вы не хотите использовать 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
 } 
0 голосов
/ 21 ноября 2015
(authorDynamic as ExpandoObject).Any(pair => pair.Key == "YourProp");
0 голосов
/ 18 июля 2012

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

public bool PropertyExist(object obj, string propertyName)
{
 return obj.GetType().GetProperty(propertyName) != null;
}
0 голосов
/ 15 мая 2010

Эй, ребята, прекратите использовать 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;
    }
}
...