Відповіді:
Згідно з 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 ви можете просто перевірити, чи властивість визначено як ключ у нижньому словнику. Для інших реалізацій це може бути складним, а іноді єдиним способом є робота з винятками. Детальніше перейдіть за посиланням вище.
ОНОВЛЕНО: Ви можете використовувати делегатів і спробувати отримати значення з властивості динамічного об'єкта, якщо воно існує. Якщо властивості немає, просто зловіть виняток і поверніть хибне.
Погляньте, для мене це добре працює:
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
}
Цей метод розширення перевіряє наявність властивості, а потім повертає значення або 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");
Залежно від випадку використання, якщо null можна вважати таким же, як невизначений, ви можете перетворити 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"
. Отже, цей код працює.