Ответы:
Согласно 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.
}
Важное различие должно быть сделано здесь.
Большинство ответов здесь относятся только к 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;
}
. . .
Вы можете увидеть его привязанным к 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
Другими словами, он всегда возвращает значение для всех ключей, независимо от того, что в нем находится - он просто возвращает ноль, когда там ничего нет. Но ViewDataDictionary имеет бремя привязки к модели MVC, поэтому лучше исключить только изящные части словаря для использования вне MVC Views.
Слишком долго, чтобы действительно публиковать здесь все кишки - большинство из них просто реализуют IDictionary - но вот динамический объект (класс DDict
), который не генерирует нулевые проверки свойств, которые не были объявлены, на Github:
https://github.com/b9chris/GracefulDynamicDictionary
Если вы просто хотите добавить его в свой проект через NuGet, его имя GracefulDynamicDictionary .
Недавно я ответил на очень похожий вопрос: как мне отразить элементы динамического объекта?
Вкратце, ExpandoObject - не единственный динамический объект, который вы можете получить. Отражение будет работать для статических типов (типов, которые не реализуют IDynamicMetaObjectProvider). Для типов, которые реализуют этот интерфейс, отражение в основном бесполезно. Для ExpandoObject вы можете просто проверить, определено ли свойство как ключ в базовом словаре. Для других реализаций это может быть сложно, а иногда единственный способ - работать с исключениями. Для получения подробной информации перейдите по ссылке выше.
ОБНОВЛЕНО: Вы можете использовать делегаты и пытаться получить значение из свойства динамического объекта, если оно существует. Если свойства нет, просто перехватите исключение и верните 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
IsPropertyExist
. В этом примере вы знаете, что можно бросить InvalidOperationException
. На практике вы не представляете, какое исключение может быть выдано. +1, чтобы противодействовать культу груза.
Я хотел создать метод расширения, чтобы я мог сделать что-то вроде:
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"))
{
...
}
Почему вы не хотите использовать 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
}
Этот метод расширения проверяет наличие свойства, а затем возвращает значение или ноль. Это полезно, если вы не хотите, чтобы ваши приложения выдавали ненужные исключения, по крайней мере, те, которые вы можете помочь.
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");
В зависимости от вашего варианта использования, если 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
(authorDynamic as ExpandoObject).Any(pair => pair.Key == "YourProp");
Эй, ребята, прекратите использовать 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;
}
}
Попробуй это
public bool PropertyExist(object obj, string propertyName)
{
return obj.GetType().GetProperty(propertyName) != null;
}
dynamic
объектами (всегда возвращает null
).
data.myProperty
; он проверяет, чтоtypeof data.myProperty
возвращается. Это верно, чтоdata.myProperty
может существовать и быть установленнымundefined
, но в этом случаеtypeof
будет возвращать что-то отличное от"undefined"
. Так что этот код работает.