Risposte:
Secondo MSDN la dichiarazione mostra che sta implementando IDictionary:
public sealed class ExpandoObject : IDynamicMetaObjectProvider,
IDictionary<string, Object>, ICollection<KeyValuePair<string, Object>>,
IEnumerable<KeyValuePair<string, Object>>, IEnumerable, INotifyPropertyChanged
Puoi usarlo per vedere se un membro è definito:
var expandoObject = ...;
if(((IDictionary<String, object>)expandoObject).ContainsKey("SomeMember")) {
// expandoObject.SomeMember exists.
}
Una distinzione importante deve essere fatta qui.
La maggior parte delle risposte qui sono specifiche per ExpandoObject menzionato nella domanda. Ma un uso comune (e un motivo per atterrare su questa domanda durante la ricerca) è quando si utilizza ASP.Net MVC ViewBag. Questa è un'implementazione / sottoclasse personalizzata di DynamicObject, che non genererà un'eccezione quando si controlla null su qualsiasi nome di proprietà arbitrario. Supponiamo che tu possa dichiarare una proprietà come:
@{
ViewBag.EnableThinger = true;
}
Supponiamo quindi che tu voglia verificarne il valore e se sia addirittura impostato, se esiste. Quanto segue è valido, verrà compilato, non genererà eccezioni e ti darà la risposta giusta:
if (ViewBag.EnableThinger != null && ViewBag.EnableThinger)
{
// Do some stuff when EnableThinger is true
}
Ora sbarazzati della dichiarazione di EnableThinger. Lo stesso codice viene compilato ed eseguito correttamente. Non c'è bisogno di riflessione.
A differenza di ViewBag, ExpandoObject verrà lanciato se si verifica la presenza di null su una proprietà che non esiste. Per ottenere la funzionalità più delicata di MVC ViewBag dai tuoi dynamic
oggetti, dovrai utilizzare un'implementazione di dinamica che non getti.
È possibile semplicemente utilizzare l'implementazione esatta in 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;
}
. . .
Puoi vederlo collegato a MVC Views qui, in MVC ViewPage:
http://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Mvc/ViewPage.cs
La chiave del comportamento elegante di DynamicViewDataDictionary è l'implementazione del dizionario su ViewDataDictionary, qui:
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
In altre parole, restituisce sempre un valore per tutte le chiavi, indipendentemente da ciò che contiene, restituisce semplicemente null quando non c'è nulla. Tuttavia, ViewDataDictionary ha l'onere di essere legato al Modello MVC, quindi è meglio eliminare solo le parti del dizionario graziose da utilizzare all'esterno di MVC Views.
È troppo lungo per pubblicare davvero tutte le budella qui - la maggior parte solo implementando IDictionary - ma ecco un oggetto dinamico (classe DDict
) che non genera controlli null su proprietà che non sono state dichiarate, su Github:
https://github.com/b9chris/GracefulDynamicDictionary
Se vuoi solo aggiungerlo al tuo progetto tramite NuGet, il suo nome è GracefulDynamicDictionary .
Di recente ho risposto a una domanda molto simile: come posso riflettere sui membri dell'oggetto dinamico?
In breve, ExpandoObject non è l'unico oggetto dinamico che potresti ottenere. Reflection funzionerebbe con tipi statici (tipi che non implementano IDynamicMetaObjectProvider). Per i tipi che implementano questa interfaccia, la riflessione è sostanzialmente inutile. Per ExpandoObject, puoi semplicemente verificare se la proprietà è definita come chiave nel dizionario sottostante. Per altre implementazioni, potrebbe essere impegnativo e talvolta l'unico modo è lavorare con le eccezioni. Per i dettagli, seguire il link sopra.
AGGIORNATO: è possibile utilizzare i delegati e provare a ottenere un valore dalla proprietà dell'oggetto dinamico se esiste. Se non è presente alcuna proprietà, è sufficiente prendere l'eccezione e restituire false.
Dai un'occhiata, funziona bene per me:
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();
}
L'output del codice è il seguente:
True
True
False
IsPropertyExist
. In questo esempio, sai che uno può lanciare un InvalidOperationException
. In pratica, non hai idea di quale eccezione possa essere generata. +1 per contrastare il culto del carico.
Volevo creare un metodo di estensione in modo da poter fare qualcosa del tipo:
dynamic myDynamicObject;
myDynamicObject.propertyName = "value";
if (myDynamicObject.HasProperty("propertyName"))
{
//...
}
... ma non è possibile creare estensioni in ExpandoObject
base alla documentazione di C # 5 (maggiori informazioni qui ).
Così ho finito per creare un aiutante di classe:
public static class ExpandoObjectHelper
{
public static bool HasProperty(ExpandoObject obj, string propertyName)
{
return ((IDictionary<String, object>)obj).ContainsKey(propertyName);
}
}
Per usarlo:
// If the 'MyProperty' property exists...
if (ExpandoObjectHelper.HasProperty(obj, "MyProperty"))
{
...
}
Perché non si desidera utilizzare Reflection per ottenere una serie di caratteri propri? Come questo
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
}
Questo metodo di estensione verifica l'esistenza di una proprietà e quindi restituisce il valore o null. Questo è utile se non vuoi che le tue applicazioni generino eccezioni non necessarie, almeno quelle che puoi aiutare.
public static object Value(this ExpandoObject expando, string name)
{
var expandoDic = (IDictionary<string, object>)expando;
return expandoDic.ContainsKey(name) ? expandoDic[name] : null;
}
Se può essere usato come tale:
// lookup is type 'ExpandoObject'
object value = lookup.Value("MyProperty");
o se la tua variabile locale è 'dinamica' dovrai prima lanciarla su ExpandoObject.
// lookup is type 'dynamic'
object value = ((ExpandoObject)lookup).Value("PropertyBeingTested");
A seconda del caso d'uso, se null può essere considerato uguale a indefinito, è possibile trasformare ExpandoObject in 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"));
Produzione:
a is 1
b is 2.5
c is undefined
(authorDynamic as ExpandoObject).Any(pair => pair.Key == "YourProp");
Ehi ragazzi smettete di usare Reflection per tutto ciò che costa molti cicli della CPU.
Ecco la soluzione:
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;
}
}
Prova questo
public bool PropertyExist(object obj, string propertyName)
{
return obj.GetType().GetProperty(propertyName) != null;
}
dynamic
oggetti (restituisce sempre null
).
data.myProperty
; controlla ciò chetypeof data.myProperty
ritorna. È corretto chedata.myProperty
possa esistere ed essere impostato suundefined
, ma in tal caso,typeof
restituirà qualcosa di diverso"undefined"
. Quindi questo codice funziona.