Odpowiedzi:
Według MSDN deklaracja pokazuje, że implementuje IDictionary:
public sealed class ExpandoObject : IDynamicMetaObjectProvider,
IDictionary<string, Object>, ICollection<KeyValuePair<string, Object>>,
IEnumerable<KeyValuePair<string, Object>>, IEnumerable, INotifyPropertyChanged
Możesz użyć tego, aby sprawdzić, czy członek jest zdefiniowany:
var expandoObject = ...;
if(((IDictionary<String, object>)expandoObject).ContainsKey("SomeMember")) {
// expandoObject.SomeMember exists.
}
Należy tu dokonać istotnego rozróżnienia.
Większość odpowiedzi tutaj jest specyficznych dla ExpandoObject, który jest wymieniony w pytaniu. Ale powszechnym zastosowaniem (i powodem, dla którego warto znaleźć to pytanie podczas wyszukiwania) jest użycie ASP.Net MVC ViewBag. Jest to niestandardowa implementacja / podklasa DynamicObject, która nie zgłosi wyjątku, gdy sprawdzisz dowolną nazwę właściwości pod kątem wartości null. Załóżmy, że możesz zadeklarować właściwość taką jak:
@{
ViewBag.EnableThinger = true;
}
Załóżmy, że chcesz sprawdzić jego wartość i czy w ogóle jest ustawiona - czy istnieje. Poniższe informacje są prawidłowe, będą się kompilować, nie będą zgłaszać żadnych wyjątków i dadzą właściwą odpowiedź:
if (ViewBag.EnableThinger != null && ViewBag.EnableThinger)
{
// Do some stuff when EnableThinger is true
}
Teraz pozbądź się deklaracji EnableThinger. Ten sam kod kompiluje się i działa poprawnie. Nie ma potrzeby refleksji.
W przeciwieństwie do ViewBag, ExpandoObject wyrzuci, jeśli zaznaczysz null dla właściwości, która nie istnieje. Aby uzyskać delikatniejszą funkcjonalność MVC ViewBag z twoich dynamic
obiektów, musisz użyć implementacji dynamiki, która nie rzuca.
Możesz po prostu użyć dokładnej implementacji w 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;
}
. . .
Możesz zobaczyć, że jest on powiązany z widokami MVC tutaj, w MVC ViewPage:
http://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Mvc/ViewPage.cs
Kluczem do wdzięcznego zachowania DynamicViewDataDictionary jest implementacja Dictionary na ViewDataDictionary, tutaj:
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
Innymi słowy, zawsze zwraca wartość dla wszystkich kluczy, niezależnie od tego, co jest w środku - po prostu zwraca wartość null, gdy nic tam nie ma. Ale ViewDataDictionary ma ciężar związany z modelem MVC, więc lepiej jest usunąć tylko pełne wdzięku części słownika do użycia poza widokami MVC.
Naprawdę za długo, żeby opublikować tutaj wszystkie wnętrzności - większość z nich to tylko implementacja IDictionary - ale tutaj jest dynamiczny obiekt (klasa DDict
), który nie wyrzuca zerowych kontroli właściwości, które nie zostały zadeklarowane, na Github:
https://github.com/b9chris/GracefulDynamicDictionary
Jeśli chcesz tylko dodać go do swojego projektu za pomocą NuGet, jego nazwa to GracefulDynamicDictionary .
Odpowiedziałem ostatnio na bardzo podobne pytanie: jak mogę zastanowić się nad elementami dynamicznego obiektu?
Krótko mówiąc, ExpandoObject nie jest jedynym dynamicznym obiektem, który możesz uzyskać. Odbicie działałoby dla typów statycznych (typy, które nie implementują IDynamicMetaObjectProvider). W przypadku typów, które implementują ten interfejs, odbicie jest w zasadzie bezużyteczne. W przypadku ExpandoObject możesz po prostu sprawdzić, czy właściwość jest zdefiniowana jako klucz w podstawowym słowniku. W przypadku innych implementacji może to być trudne, a czasem jedynym sposobem jest praca z wyjątkami. Aby uzyskać szczegółowe informacje, kliknij powyższy link.
ZAKTUALIZOWANO: Możesz użyć delegatów i spróbować uzyskać wartość z właściwości obiektu dynamicznego, jeśli istnieje. Jeśli nie ma właściwości, po prostu złap wyjątek i zwróć false.
Spójrz, działa dla mnie dobrze:
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();
}
Kod wyjściowy jest następujący:
True
True
False
IsPropertyExist
. W tym przykładzie wiesz, że można rzucić InvalidOperationException
. W praktyce nie masz pojęcia, jaki wyjątek może zostać zgłoszony. +1, aby przeciwdziałać kultowi ładunku.
Chciałem stworzyć metodę rozszerzenia, aby móc zrobić coś takiego:
dynamic myDynamicObject;
myDynamicObject.propertyName = "value";
if (myDynamicObject.HasProperty("propertyName"))
{
//...
}
... ale nie możesz tworzyć rozszerzeń ExpandoObject
zgodnie z dokumentacją w C # 5 (więcej informacji tutaj ).
Więc ostatecznie stworzyłem pomocnika klasy:
public static class ExpandoObjectHelper
{
public static bool HasProperty(ExpandoObject obj, string propertyName)
{
return ((IDictionary<String, object>)obj).ContainsKey(propertyName);
}
}
Aby go użyć:
// If the 'MyProperty' property exists...
if (ExpandoObjectHelper.HasProperty(obj, "MyProperty"))
{
...
}
Dlaczego nie chcesz używać Reflection, aby uzyskać zestaw propery typu? Lubię to
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
}
Ta metoda rozszerzenia sprawdza istnienie właściwości, a następnie zwraca wartość lub wartość null. Jest to przydatne, jeśli nie chcesz, aby aplikacje zgłaszały niepotrzebne wyjątki, przynajmniej takie, w których możesz pomóc.
public static object Value(this ExpandoObject expando, string name)
{
var expandoDic = (IDictionary<string, object>)expando;
return expandoDic.ContainsKey(name) ? expandoDic[name] : null;
}
Jeśli może być używany jako taki:
// lookup is type 'ExpandoObject'
object value = lookup.Value("MyProperty");
lub jeśli twoja zmienna lokalna jest „dynamiczna”, musisz najpierw rzucić ją na ExpandoObject.
// lookup is type 'dynamic'
object value = ((ExpandoObject)lookup).Value("PropertyBeingTested");
W zależności od przypadku użycia, jeśli wartość null może być uznana za taką samą jak niezdefiniowana, możesz zmienić ExpandoObject w 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"));
Wynik:
a is 1
b is 2.5
c is undefined
(authorDynamic as ExpandoObject).Any(pair => pair.Key == "YourProp");
Hej, przestańcie używać Reflection do wszystkiego, co kosztuje dużo cykli procesora.
Oto rozwiązanie:
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;
}
}
Spróbuj tego
public bool PropertyExist(object obj, string propertyName)
{
return obj.GetType().GetProperty(propertyName) != null;
}
dynamic
obiektami (zawsze zwraca null
).
data.myProperty
; sprawdza, cotypeof data.myProperty
zwraca. To prawda, żedata.myProperty
może istnieć i być ustawioneundefined
, ale w takim przypadkutypeof
zwróci coś innego niż"undefined"
. Więc ten kod działa.