Wie erkenne ich, ob eine Eigenschaft in einem ExpandoObject vorhanden ist?

188

In Javascript können Sie mithilfe des undefinierten Schlüsselworts feststellen, ob eine Eigenschaft definiert ist:

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

Wie würden Sie dies in C # tun, indem Sie das dynamische Schlüsselwort mit ExpandoObjectund ohne Ausnahmebedingung verwenden?

Softlion
quelle
5
@CodeInChaos: Beachten Sie, dass der angezeigte Code den Wert von nicht überprüft data.myProperty. es prüft, was typeof data.myPropertyzurückkommt. Es ist richtig, dass es data.myPropertymöglicherweise existiert und auf gesetzt ist undefined, aber in diesem Fall typeofwird etwas anderes als zurückgegeben "undefined". Dieser Code funktioniert also.
Aasmund Eldhuset

Antworten:

181

Laut MSDN zeigt die Erklärung, dass IDictionary implementiert wird:

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

Sie können dies verwenden, um festzustellen, ob ein Mitglied definiert ist:

var expandoObject = ...;
if(((IDictionary<String, object>)expandoObject).ContainsKey("SomeMember")) {
    // expandoObject.SomeMember exists.
}
Dykam
quelle
3
Um diese Überprüfung zu vereinfachen, habe ich TryGetValue überladen und immer true zurückgegeben. Der Rückgabewert wurde auf "undefined" gesetzt, wenn die Eigenschaft nicht definiert wurde. if (someObject.someParam! = "undefined") ... und es funktioniert :)
Softlion
Auch möglich :), aber ich denke du meintest "überschrieben" anstatt überladen.
Dykam
ja. Ich habe wieder "undefined" in den const-Wert eines speziellen Objekts geändert, den ich an anderer Stelle erstellt habe. Es verhindert Casting-Probleme: p
Softlion
1
Ich glaube, diese Lösung ist immer noch aktuell. Nehmen Sie nicht das Wort von jemandem für den Preis der Reflexion - testen Sie es selbst und sehen Sie, ob Sie es sich leisten können
nik.shornikov
1
@ BlueRaja-DannyPflughoeft Ja, das ist es. Ohne die Besetzung wird es nur ein dynamischer Aufruf sein, mit dem Fall, dass Sie in den Interna bekommen.
Insbesondere
28

Hier muss eine wichtige Unterscheidung getroffen werden.

Die meisten Antworten hier beziehen sich speziell auf das ExpandoObject, das in der Frage erwähnt wird. Eine häufige Verwendung (und ein Grund, bei der Suche auf diese Frage zu stoßen) ist die Verwendung des ASP.Net MVC ViewBag. Dies ist eine benutzerdefinierte Implementierung / Unterklasse von DynamicObject, die keine Ausnahme auslöst, wenn Sie einen beliebigen Eigenschaftsnamen auf null prüfen. Angenommen, Sie deklarieren eine Eigenschaft wie:

@{
    ViewBag.EnableThinger = true;
}

Angenommen, Sie möchten den Wert überprüfen und prüfen, ob er überhaupt festgelegt ist - ob er vorhanden ist. Folgendes ist gültig, wird kompiliert, wirft keine Ausnahmen und gibt Ihnen die richtige Antwort:

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

Entfernen Sie jetzt die Deklaration von EnableThinger. Der gleiche Code wird kompiliert und ordnungsgemäß ausgeführt. Keine Notwendigkeit zum Nachdenken.

Im Gegensatz zu ViewBag wird ExpandoObject ausgelöst, wenn Sie eine nicht vorhandene Eigenschaft auf Null prüfen. Um die sanftere Funktionalität von MVC ViewBag aus Ihren dynamicObjekten herauszuholen , müssen Sie eine Implementierung von Dynamic verwenden, die nicht funktioniert.

Sie können einfach die genaue Implementierung in MVC ViewBag verwenden:

. . .
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

Sie können sehen, dass es in MVC Views hier in MVC ViewPage eingebunden ist:

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

Der Schlüssel für das elegante Verhalten von DynamicViewDataDictionary ist die Dictionary-Implementierung in 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

Mit anderen Worten, es gibt immer einen Wert für alle Schlüssel zurück, unabhängig davon, was darin enthalten ist - es gibt einfach null zurück, wenn nichts da ist. ViewDataDictionary muss jedoch an das MVC-Modell gebunden sein. Daher ist es besser, nur die eleganten Wörterbuchteile für die Verwendung außerhalb von MVC Views zu entfernen.

Es ist zu lang, um wirklich alle Eingeweide hier zu veröffentlichen - das meiste davon implementiert nur IDictionary -, aber hier ist ein dynamisches Objekt (Klasse DDict), das keine Nullprüfungen für nicht deklarierte Eigenschaften auf Github auslöst:

https://github.com/b9chris/GracefulDynamicDictionary

Wenn Sie es nur über NuGet zu Ihrem Projekt hinzufügen möchten, heißt es GracefulDynamicDictionary .

Chris Moschini
quelle
Warum haben Sie sich für DynamicDictionary entschieden, da es dann keine Reflexion verwendet?
Softlion
dann können Sie es abstimmen, da dies die gleiche Lösung ist :)
Softlion
3
Es ist sicherlich nicht die gleiche Lösung.
Chris Moschini
"Sie müssen eine ähnliche Unterklasse erstellen, die nicht ausgelöst wird, wenn eine Eigenschaft nicht gefunden wird." => es ist! Oh nein, ist es nicht. Meine Lösung ist besser. Es wirft - weil wir es wollen, UND es kann auch nicht werfen, wenn TryXX verwendet wird;
Softlion
1
Dies ist genau der Grund, warum ich hier bin. Ich konnte nicht herausfinden, warum ein Code (Viewbag) NICHT kaputt ging. Danke.
Adam Tolley
11

Ich habe kürzlich eine sehr ähnliche Frage beantwortet: Wie reflektiere ich über die Mitglieder eines dynamischen Objekts?

In Kürze ist ExpandoObject nicht das einzige dynamische Objekt, das Sie erhalten könnten. Die Reflexion würde für statische Typen funktionieren (Typen, die IDynamicMetaObjectProvider nicht implementieren). Für Typen, die diese Schnittstelle implementieren, ist die Reflexion grundsätzlich nutzlos. Bei ExpandoObject können Sie einfach überprüfen, ob die Eigenschaft als Schlüssel im zugrunde liegenden Wörterbuch definiert ist. Bei anderen Implementierungen kann dies eine Herausforderung sein, und manchmal besteht die einzige Möglichkeit darin, mit Ausnahmen zu arbeiten. Für Details folgen Sie dem Link oben.

Alexandra Rusina
quelle
11

AKTUALISIERT: Sie können Delegaten verwenden und versuchen, einen Wert aus der dynamischen Objekteigenschaft abzurufen, falls vorhanden. Wenn keine Eigenschaft vorhanden ist, fangen Sie einfach die Ausnahme ab und geben Sie false zurück.

Schau mal, es funktioniert gut für mich:

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

Die Ausgabe des Codes ist die folgende:

True
True
False
Alexander G.
quelle
2
@marklam Es ist schlecht, alle Ausnahmen abzufangen, wenn Sie nicht wissen, was die Ausnahme verursacht. In unseren Fällen ist es in Ordnung, da wir möglicherweise das Fehlen eines Feldes erwarten.
Alexander G
3
Wenn Sie wissen, was die Ausnahme verursacht, müssen Sie auch den Typ kennen, also catch (WhateverException). Andernfalls wird Ihr Code stillschweigend fortgesetzt, selbst wenn Sie eine unerwartete Ausnahme haben - wie zum Beispiel OutOfMemoryException.
Marklam
4
Sie können jeden Getter an übergeben IsPropertyExist. In diesem Beispiel wissen Sie, dass man einen werfen kann InvalidOperationException. In der Praxis haben Sie keine Ahnung, welche Ausnahme ausgelöst werden kann. +1, um dem Frachtkult entgegenzuwirken.
Piedar
2
Diese Lösung ist nicht akzeptabel, wenn die Leistung wichtig ist. Wenn sie beispielsweise in einer Schleife mit mehr als 500 Iterationen verwendet wird, summiert sie sich und kann viele Sekunden Verzögerung verursachen. Jedes Mal, wenn eine Ausnahme abgefangen wird, muss der Stapel in das Ausnahmeobjekt kopiert werden
Jim109
1
Betreff: Leistung: Der angehängte Debugger und Console.WriteLine sind die langsamen Bits. 10.000 Iterationen dauern hier weniger als 200 ms (mit 2 Ausnahmen pro Iteration). Der gleiche Test dauert ausnahmslos eine Handvoll Millisekunden. Das heißt, wenn Sie erwarten, dass bei der Verwendung dieses Codes nur selten eine Eigenschaft fehlt, oder wenn Sie sie nur eine begrenzte Anzahl von Malen aufrufen oder die Ergebnisse zwischenspeichern können, stellen Sie bitte fest, dass alles seinen Platz hat und keines der übermäßig - Aufgeregte Warnungen sind hier wichtig.
Chaos
10

Ich wollte eine Erweiterungsmethode erstellen , damit ich Folgendes tun kann:

dynamic myDynamicObject;
myDynamicObject.propertyName = "value";

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

... aber Sie können keine Erweiterungen ExpandoObjectgemäß der C # 5-Dokumentation erstellen (weitere Informationen hier ).

Also habe ich einen Klassenhelfer geschaffen:

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

Um es zu benutzen:

// If the 'MyProperty' property exists...
if (ExpandoObjectHelper.HasProperty(obj, "MyProperty"))
{
    ...
}
Maxime
quelle
4
Stimmen Sie für nützliche Kommentare und Links zu Erweiterungen für ExpandoObject ab.
Roberto
1

Warum möchten Sie Reflection nicht verwenden, um eine Reihe von Typ-Eigenschaften zu erhalten? So was

 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
 } 
Vokinneberg
quelle
Ich bin nicht sicher, ob dadurch die dynamisch hinzugefügten Eigenschaften zurückgegeben werden. Ich vermute, dass die Methoden des dynamischen Objekts zurückgegeben werden.
Dykam
1

Diese Erweiterungsmethode prüft, ob eine Eigenschaft vorhanden ist, und gibt dann den Wert oder null zurück. Dies ist nützlich, wenn Sie nicht möchten, dass Ihre Anwendungen unnötige Ausnahmen auslösen, zumindest solche, denen Sie helfen können.

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

Wenn als solches verwendet werden kann:

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

oder wenn Ihre lokale Variable 'dynamisch' ist, müssen Sie sie zuerst in ExpandoObject umwandeln.

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

Abhängig von Ihrem Anwendungsfall können Sie Ihr ExpandoObject in ein DynamicJsonObject verwandeln, wenn null als mit undefiniert identisch angesehen werden kann.

    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"));

Ausgabe:

a is 1
b is 2.5
c is undefined
Wolfgang Grinfeld
quelle
-2
(authorDynamic as ExpandoObject).Any(pair => pair.Key == "YourProp");
Kasper Roma
quelle
-3

Hey Leute, hört auf, Reflection für alles zu verwenden, was viele CPU-Zyklen kostet.

Hier ist die Lösung:

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;
    }
}
Softlion
quelle
4
Dies zeigt, wie ein dynamisches Objekt implementiert wird und nicht, wie eine Eigenschaft für ein dynamisches Objekt beendet wird.
Matt Warren
Sie können überprüfen, ob eine dynamische Instanz eine Eigenschaft hat, indem Sie eine Nullprüfung für die betreffende Eigenschaft durchführen.
Ctorx
2
"Dies zeigt, wie ein dynamisches Objekt implementiert wird": Ja, tatsächlich ist es das. Die Lösung für diese Frage lautet: Es gibt keine generische Lösung, da dies von der Implementierung abhängt.
Softlion
@ Softlion Nein, die Lösung ist das, was wir nicht mehr verwenden müssen
nik.shornikov
@Softlion Was ist der Sinn der Tryxxx-Methoden? TryGet gibt niemals false zurück, wenn die Eigenschaft nicht gefunden wird. Sie müssen das Ergebnis also noch überprüfen. Die Rückgabe ist nutzlos. Wenn der Schlüssel in TrySet nicht vorhanden ist, wird eine Ausnahme ausgelöst, anstatt false zurückzugeben. Ich verstehe nicht, warum Sie dies überhaupt als Antwort verwenden würden, wenn Sie selbst hier zu den Kommentaren "Die Lösung für diese Frage lautet: Es gibt keine generische Lösung, da dies von der Implementierung abhängt" geschrieben haben, das stimmt auch nicht. Schauen Sie sich Dykams Antwort an, um die richtige Lösung zu finden.
pqsk
-5

Probier diese

public bool PropertyExist(object obj, string propertyName)
{
 return obj.GetType().GetProperty(propertyName) != null;
}
Venkat
quelle
3
Dies würde das Vorhandensein einer Eigenschaft des Objekts überprüfen, das unter dem dynamischen Namen verborgen ist, der ein Implementierungsdetail darstellt. Haben Sie Ihre Lösung vor dem Posten in echtem Code überprüft? Es sollte überhaupt nicht funktionieren.
Softlion
Ich habe diesen Code in Echtzeit verwendet. Es funktioniert gut.
Venkat
6
Gibt mir die ganze Zeit null, auch wenn die Eigenschaft existiert.
Atlantis
Es würde mit einfachen Objekten funktionieren, aber nicht mit ExpandoObjects. Dynamik, nicht sicher.
Joe
1
Zur Bestätigung funktioniert dies auch nicht mit dynamicObjekten (wird immer zurückgegeben null).
Gone Coding