web-dev-qa-db-fra.com

Comment détecter si une propriété existe sur un ExpandoObject?

En javascript, vous pouvez détecter si une propriété est définie à l'aide du mot clé undefined:

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

Comment feriez-vous cela en C # en utilisant le mot-clé dynamic avec un ExpandoObject et sans lever d'exception?

177
Softlion

Selon MSDN la déclaration indique qu'elle implémente IDictionary:

public sealed class ExpandoObject : IDynamicMetaObjectProvider, 
    IDictionary<string, Object>, ICollection<KeyValuePair<string, Object>>, 
    IEnumerable<KeyValuePair<string, Object>>, IEnumerable, INotifyPropertyChanged

Vous pouvez utiliser ceci pour voir si un membre est défini:

var expandoObject = ...;
if(((IDictionary<String, object>)expandoObject).ContainsKey("SomeMember")) {
    // expandoObject.SomeMember exists.
}
176
Dykam

Une distinction importante doit être faite ici.

La plupart des réponses ici sont spécifiques à ExpandoObject qui est mentionné dans la question. Cependant, l'utilisation du ViewBag ASP.Net MVC est un usage courant (et une raison d'atterrir sur cette question lors d'une recherche). Il s'agit d'une implémentation/sous-classe personnalisée de DynamicObject, qui ne génère pas d'exception lorsque vous recherchez la valeur null dans tout nom de propriété arbitraire. Supposons que vous puissiez déclarer une propriété telle que:

@{
    ViewBag.EnableThinger = true;
}

Supposons ensuite que vous souhaitiez vérifier sa valeur et si elle est définie - existe-t-elle? Ce qui suit est valide, va compiler, ne lève aucune exception et vous donne la bonne réponse:

if (ViewBag.EnableThinger != null && ViewBag.EnableThinger)
{
    // Do some stuff when EnableThinger is true
}

Maintenant, supprimez la déclaration de EnableThinger. Le même code est compilé et fonctionne correctement. Pas besoin de réflexion.

Contrairement à ViewBag, ExpandoObject lancera si vous recherchez la valeur null sur une propriété qui n'existe pas. Afin de tirer la fonctionnalité plus douce de MVC ViewBag de vos objets dynamic, vous devez utiliser une implémentation de dynamic qui ne jette pas.

Vous pouvez simplement utiliser l'implémentation exacte dans 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

Vous pouvez voir qu'il est lié aux vues MVC ici, dans MVC ViewPage:

http://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Mvc/ViewPage.cs

La clé du comportement gracieux de DynamicViewDataDictionary est l’implémentation du dictionnaire sur ViewDataDictionary, ici:

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

En d'autres termes, il renvoie toujours une valeur pour toutes les clés, quel que soit son contenu. Il renvoie simplement null lorsqu'il n'y a rien. Cependant, ViewDataDictionary a le fardeau d'être lié au modèle de MVC, il est donc préférable de supprimer les parties gracieuses du dictionnaire pour une utilisation en dehors des vues MVC.

Il est trop long de publier tous les tripes ici - la plupart implémentant simplement IDictionary - mais voici un objet dynamique qui ne lance pas de contrôle nul sur les propriétés qui n'ont pas été déclarées, sur Github:

https://github.com/b9chris/GracefulDynamicDictionary

Si vous souhaitez simplement l'ajouter à votre projet via NuGet, son nom est GracefulDynamicDictionary .

27
Chris Moschini

J'ai récemment répondu à une question très similaire: Comment puis-je réfléchir sur les membres d'un objet dynamique?

En bref, ExpandoObject n'est pas le seul objet dynamique que vous pourriez obtenir. Reflection fonctionnerait pour les types statiques (types qui n'implémentent pas IDynamicMetaObjectProvider). Pour les types qui implémentent cette interface, la réflexion est fondamentalement inutile. Pour ExpandoObject, vous pouvez simplement vérifier si la propriété est définie en tant que clé dans le dictionnaire sous-jacent. Pour d'autres implémentations, cela peut être difficile et parfois le seul moyen est de travailler avec des exceptions. Pour plus de détails, suivez le lien ci-dessus.

11
Alexandra Rusina

MISE À JOUR: Vous pouvez utiliser des délégués et essayer d'obtenir une valeur de la propriété d'objet dynamique, si elle existe. S'il n'y a pas de propriété, attrapez simplement l'exception et renvoyez false.

Regardez, ça marche très bien pour moi:

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();
}

La sortie du code est la suivante:

True
True
False
10
Alexander G

Je voulais créer une méthode d'extension pour pouvoir faire quelque chose comme:

dynamic myDynamicObject;
myDynamicObject.propertyName = "value";

if (myDynamicObject.HasProperty("propertyName"))
{
    //...
}

... mais vous ne pouvez pas créer d'extensions sur ExpandoObject selon la documentation de C # 5 (plus d'information ici ).

Alors j'ai fini par créer un assistant de classe:

public static class ExpandoObjectHelper
{
    public static bool HasProperty(ExpandoObject obj, string propertyName)
    {
        return ((IDictionary<String, object>)obj).ContainsKey(propertyName);
    }
}

Pour l'utiliser:

// If the 'MyProperty' property exists...
if (ExpandoObjectHelper.HasProperty(obj, "MyProperty"))
{
    ...
}
8
Maxime

Pourquoi ne voulez-vous pas utiliser Reflection pour obtenir un ensemble de propriétés de type? Comme ça

 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
 } 
1
Vokinneberg

Cette méthode d'extension vérifie l'existence d'une propriété, puis renvoie la valeur ou la valeur null. Cela est utile si vous ne souhaitez pas que vos applications génèrent des exceptions inutiles, du moins celles que vous pouvez aider.

    public static object Value(this ExpandoObject expando, string name)
    {
        var expandoDic = (IDictionary<string, object>)expando;
        return expandoDic.ContainsKey(name) ? expandoDic[name] : null;
    }

Si peut être utilisé comme tel:

  // lookup is type 'ExpandoObject'
  object value = lookup.Value("MyProperty");

ou si votre variable locale est 'dynamique', vous devrez d'abord la convertir en ExpandoObject.

  // lookup is type 'dynamic'
  object value = ((ExpandoObject)lookup).Value("PropertyBeingTested");
1
realdanielbyrne