it-swarm.com.de

Testen Sie, ob eine Eigenschaft für eine dynamische Variable verfügbar ist

Meine Situation ist sehr einfach. Irgendwo in meinem Code habe ich Folgendes:

dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame();

//How to do this?
if (myVariable.MyProperty.Exists)   
//Do stuff

Im Grunde ist meine Frage, wie Sie überprüfen können (ohne eine Ausnahme auszulösen), ob eine bestimmte Eigenschaft für meine dynamische Variable verfügbar ist. Ich könnte GetType() tun, aber ich würde das lieber vermeiden, da ich nicht wirklich den Typ des Objekts kennen muss. Alles, was ich wirklich wissen möchte, ist, ob eine Eigenschaft (oder Methode, wenn das Leben einfacher ist) verfügbar ist. Irgendwelche Hinweise?

195
roundcrisis

Ich denke, es gibt keine Möglichkeit herauszufinden, ob eine Variable dynamic ein bestimmtes Member hat, ohne zu versuchen, darauf zuzugreifen, es sei denn, Sie haben die Art und Weise, wie die dynamische Bindung im C # -Compiler verarbeitet wird, erneut implementiert. Das würde wahrscheinlich viel raten, da es gemäß der C # -Spezifikation implementierungsdefiniert ist.

Sie sollten also tatsächlich versuchen, auf das Member zuzugreifen und eine Ausnahme abzufangen, falls dies fehlschlägt:

dynamic myVariable = GetDataThatLooksVerySimilarButNotTheSame();

try
{
    var x = myVariable.MyProperty;
    // do stuff with x
}
catch (RuntimeBinderException)
{
    //  MyProperty doesn't exist
} 
142
svick

Ich dachte ich würde einen Vergleich von Martijns Antwort machen und svicks Antwort ...

Das folgende Programm liefert die folgenden Ergebnisse:

Testing with exception: 2430985 ticks
Testing with reflection: 155570 ticks

void Main()
{
    var random = new Random(Environment.TickCount);

    dynamic test = new Test();

    var sw = new Stopwatch();

    sw.Start();

    for (int i = 0; i < 100000; i++)
    {
        TestWithException(test, FlipCoin(random));
    }

    sw.Stop();

    Console.WriteLine("Testing with exception: " + sw.ElapsedTicks.ToString() + " ticks");

    sw.Restart();

    for (int i = 0; i < 100000; i++)
    {
        TestWithReflection(test, FlipCoin(random));
    }

    sw.Stop();

    Console.WriteLine("Testing with reflection: " + sw.ElapsedTicks.ToString() + " ticks");
}

class Test
{
    public bool Exists { get { return true; } }
}

bool FlipCoin(Random random)
{
    return random.Next(2) == 0;
}

bool TestWithException(dynamic d, bool useExisting)
{
    try
    {
        bool result = useExisting ? d.Exists : d.DoesntExist;
        return true;
    }
    catch (Exception)
    {
        return false;
    }
}

bool TestWithReflection(dynamic d, bool useExisting)
{
    Type type = d.GetType();

    return type.GetProperties().Any(p => p.Name.Equals(useExisting ? "Exists" : "DoesntExist"));
}

Als Ergebnis empfehle ich die Reflexion. Siehe unten.


Antwort auf den Kommentar von bland:

Verhältnisse sind reflection:exception-Ticks für 100000 Iterationen:

Fails 1/1: - 1:43 ticks
Fails 1/2: - 1:22 ticks
Fails 1/3: - 1:14 ticks
Fails 1/5: - 1:9 ticks
Fails 1/7: - 1:7 ticks
Fails 1/13: - 1:4 ticks
Fails 1/17: - 1:3 ticks
Fails 1/23: - 1:2 ticks
...
Fails 1/43: - 1:2 ticks
Fails 1/47: - 1:1 ticks

... fair genug - wenn Sie mit einer Wahrscheinlichkeit von weniger als ~ 1/47 versagen, dann gehen Sie zur Ausnahme.


Bei den obigen Ausführungen wird davon ausgegangen, dass Sie jedes Mal GetProperties() ausführen. Sie können den Vorgang möglicherweise beschleunigen, indem Sie das Ergebnis von GetProperties() für jeden Typ in einem Wörterbuch oder in einem ähnlichen Verzeichnis zwischenspeichern. Dies kann hilfreich sein, wenn Sie immer wieder dieselben Typen prüfen.

64
dav_i

Vielleicht Reflexion verwenden?

dynamic myVar = GetDataThatLooksVerySimilarButNotTheSame();
Type typeOfDynamic = myVar.GetType();
bool exist = typeOfDynamic.GetProperties().Where(p => p.Name.Equals("PropertyName")).Any(); 
43
Martijn

Nur für den Fall, dass es jemandem hilft:

Wenn die Methode GetDataThatLooksVerySimilarButNotTheSame() eine ExpandoObject zurückgibt, können Sie sie vor der Überprüfung auch in eine IDictionary umwandeln. 

dynamic test = new System.Dynamic.ExpandoObject();
test.foo = "bar";

if (((IDictionary<string, object>)test).ContainsKey("foo"))
{
    Console.WriteLine(test.foo);
}
30
karask

Nun, ich hatte ein ähnliches Problem, aber bei Unit-Tests.

Mit SharpTestsEx können Sie prüfen, ob eine Eigenschaft existiert. Ich verwende das Testen meiner Controller. Da das JSON-Objekt dynamisch ist, kann jemand den Namen ändern und vergessen, ihn im Javascript oder in etwas anderem zu ändern. Das Testen auf alle Eigenschaften beim Schreiben des Controllers sollte meine Sicherheit erhöhen.

Beispiel:

dynamic testedObject = new ExpandoObject();
testedObject.MyName = "I am a testing object";

Jetzt mit SharTestsEx:

Executing.This(delegate {var unused = testedObject.MyName; }).Should().NotThrow();
Executing.This(delegate {var unused = testedObject.NotExistingProperty; }).Should().Throw();

Damit teste ich alle vorhandenen Eigenschaften mit "Should (). NotThrow ()".

Es ist wahrscheinlich kein Thema, kann aber für jemanden nützlich sein.

7
Diego Santin

Die zwei gebräuchlichsten Lösungen hierfür sind das Tätigen des Anrufs und das Abfangen der RuntimeBinderException, Verwenden von Reflection zur Überprüfung des Anrufs oder das Serialisieren in ein Textformat und das Parsen von dort aus. Das Problem mit Ausnahmen besteht darin, dass sie sehr langsam sind, da der aktuelle Aufrufstapel beim Aufbau serialisiert wird. Die Serialisierung nach JSON oder etwas Ähnlichem verursacht eine ähnliche Strafe. Dies lässt uns mit Nachdenken, aber es funktioniert nur, wenn das zugrunde liegende Objekt tatsächlich ein POCO mit echten Mitgliedern ist. Wenn es sich um einen dynamischen Wrapper für ein Wörterbuch, ein COM-Objekt oder einen externen Webdienst handelt, hilft Reflektion nicht.

Eine andere Lösung besteht darin, die Variable DynamicMetaObject zu verwenden, um die Mitgliedsnamen so zu erhalten, wie sie vom DLR angezeigt werden. Im folgenden Beispiel verwende ich eine statische Klasse (Dynamic), um das Feld Age zu testen und anzuzeigen.

class Program
{
    static void Main()
    {
        dynamic x = new ExpandoObject();

        x.Name = "Damian Powell";
        x.Age = "21 (probably)";

        if (Dynamic.HasMember(x, "Age"))
        {
            Console.WriteLine("Age={0}", x.Age);
        }
    }
}

public static class Dynamic
{
    public static bool HasMember(object dynObj, string memberName)
    {
        return GetMemberNames(dynObj).Contains(memberName);
    }

    public static IEnumerable<string> GetMemberNames(object dynObj)
    {
        var metaObjProvider = dynObj as IDynamicMetaObjectProvider;

        if (null == metaObjProvider) throw new InvalidOperationException(
            "The supplied object must be a dynamic object " +
            "(i.e. it must implement IDynamicMetaObjectProvider)"
        );

        var metaObj = metaObjProvider.GetMetaObject(
            Expression.Constant(metaObjProvider)
        );

        var memberNames = metaObj.GetDynamicMemberNames();

        return memberNames;
    }
}
7
Damian Powell

Denis 'Antwort brachte mich zu einer anderen Lösung mit JsonObjects.

eine Eigenschaftsüberprüfung für Header:

Predicate<object> hasHeader = jsonObject =>
                                 ((JObject)jsonObject).OfType<JProperty>()
                                     .Any(prop => prop.Name == "header");

oder vielleicht besser:

Predicate<object> hasHeader = jsonObject =>
                                 ((JObject)jsonObject).Property("header") != null;

zum Beispiel:

dynamic json = JsonConvert.DeserializeObject(data);
string header = hasHeader(json) ? json.header : null;
5
Charles HETIER

Für mich funktioniert das:

if (IsProperty(() => DynamicObject.MyProperty))
  ; // do stuff



delegate string GetValueDelegate();

private bool IsProperty(GetValueDelegate getValueMethod)
{
    try
    {
        //we're not interesting in the return value.
        //What we need to know is whether an exception occurred or not

        var v = getValueMethod();
        return (v == null) ? false : true;
    }
    catch (RuntimeBinderException)
    {
        return false;
    }
    catch
    {
        return true;
    }
}
2
Jester

Nach der Antwort von @karask könnte man die Funktion als Helfer so einpacken:

public static bool HasProperty(ExpandoObject expandoObj,
                               string name)
{
    return ((IDictionary<string, object>)expandoObj).ContainsKey(name);
}
2
Wolfshead

Ich weiß, dass dies ein sehr alter Beitrag ist, aber hier ist eine einfache Lösung, um mit dynamic zu arbeiten. Geben Sie c# ein.

  1. kann einfache Reflexion verwenden, um direkte Eigenschaften aufzulisten
  2. oder kann die Erweiterungsmethode object verwenden
  3. oder verwenden Sie die GetAsOrDefault<int>-Methode, um ein neues, stark typisiertes Objekt mit einem Wert abzurufen, falls vorhanden, oder standardmäßig, falls nicht vorhanden.
public static class DynamicHelper
{
    private static void Test( )
    {
        dynamic myobj = new
                        {
                            myInt = 1,
                            myArray = new[ ]
                                      {
                                          1, 2.3
                                      },
                            myDict = new
                                     {
                                         myInt = 1
                                     }
                        };

        var myIntOrZero = myobj.GetAsOrDefault< int >( ( Func< int > )( ( ) => myobj.noExist ) );
        int? myNullableInt = GetAs< int >( myobj, ( Func< int > )( ( ) => myobj.myInt ) );

        if( default( int ) != myIntOrZero )
            Console.WriteLine( $"myInt: '{myIntOrZero}'" );

        if( default( int? ) != myNullableInt )
            Console.WriteLine( $"myInt: '{myNullableInt}'" );

        if( DoesPropertyExist( myobj, "myInt" ) )
            Console.WriteLine( $"myInt exists and it is: '{( int )myobj.myInt}'" );
    }

    public static bool DoesPropertyExist( dynamic dyn, string property )
    {
        var t = ( Type )dyn.GetType( );
        var props = t.GetProperties( );
        return props.Any( p => p.Name.Equals( property ) );
    }

    public static object GetAs< T >( dynamic obj, Func< T > lookup )
    {
        try
        {
            var val = lookup( );
            return ( T )val;
        }
        catch( RuntimeBinderException ) { }

        return null;
    }

    public static T GetAsOrDefault< T >( this object obj, Func< T > test )
    {
        try
        {
            var val = test( );
            return ( T )val;
        }
        catch( RuntimeBinderException ) { }

        return default( T );
    }
}
0
SimperT

In meinem Fall musste ich das Vorhandensein einer Methode mit einem bestimmten Namen überprüfen. Daher habe ich eine Schnittstelle dafür verwendet

var plugin = this.pluginFinder.GetPluginIfInstalled<IPlugin>(pluginName) as dynamic;
if (plugin != null && plugin is ICustomPluginAction)
{
    plugin.CustomPluginAction(action);
}

Schnittstellen können auch mehr als nur Methoden enthalten:

Schnittstellen können Methoden, Eigenschaften, Ereignisse, Indexer oder beliebige .__ enthalten. Kombination dieser vier Mitgliedstypen.

Von: Schnittstellen (C # -Programmierhandbuch)

Elegant und keine Notwendigkeit, Ausnahmen einzufangen oder mit Reflexion zu spielen ...

0
Fred Mauroy

Wenn Sie den als dynamisch verwendeten Typ steuern, können Sie nicht für jeden Eigenschaftszugriff einen Tuple anstelle eines Werts zurückgeben? So etwas wie...

public class DynamicValue<T>
{
    internal DynamicValue(T value, bool exists)
    {
         Value = value;
         Exists = exists;
    }

    T Value { get; private set; }
    bool Exists { get; private set; }
}

Möglicherweise eine naive Implementierung. Wenn Sie jedoch jedes Mal intern eine davon erstellen und anstelle des tatsächlichen Werts zurückgeben, können Sie Exists bei jedem Eigenschaftszugriff überprüfen und dann auf Value klicken, wenn der Wert default(T) (und irrelevant) ist, falls dies nicht der Fall ist t.

Allerdings vermisse ich vielleicht etwas Wissen darüber, wie dynamisch funktioniert, und dies ist möglicherweise kein praktikabler Vorschlag.

0
Shibumi