2010-06-06 3 views
31

Ich möchte eine einfache Ente eingeben Beispiel in C# mit dynamischen Objekten. Es scheint mir, dass ein dynamisches Objekt HasValue/HasProperty/HasMethod-Methoden mit einem einzelnen String-Parameter für den Namen des Wertes, der Eigenschaft oder der Methode haben sollte, nach dem Sie suchen, bevor Sie versuchen, dagegen zu laufen. Ich versuche zu vermeiden, versuchen/fangen Blöcke und tiefer Reflexion, wenn möglich. Es scheint nur eine gängige Praxis zu sein, in dynamischen Sprachen (JS, Ruby, Python usw.) zu tippen, um nach einer Eigenschaft/Methode zu suchen, bevor man versucht, sie zu benutzen, dann auf einen Standard zurückzufallen oder eine kontrollierte Ausnahme auszulösen . Das folgende Beispiel ist im Grunde das, was ich erreichen möchte.Ducktyp testen mit C# 4 für dynamische Objekte

Wenn die oben beschriebenen Methoden nicht existieren, hat jemand vorgefertigte Erweiterungsmethoden für dynamische, die dies tun?


Beispiel: In JavaScript kann ich relativ einfach eine Methode auf einem Objekt testen.

//JavaScript 
function quack(duck) { 
    if (duck && typeof duck.quack === "function") { 
    return duck.quack(); 
    } 
    return null; //nothing to return, not a duck 
}


Wie würde ich das Gleiche tun in C#?

//C# 4 
dynamic Quack(dynamic duck) 
{ 
    //how do I test that the duck is not null, 
    //and has a quack method? 

    //if it doesn't quack, return null 
}
+0

Nur als eine Anmerkung für jeden auf der Suche ... ExpandoObject (unsicher von anderen) implementiert IDictionary , so dass Sie mit var myDynamicAsDictionary = myDyn als IDictionary ; Testen Sie dann auf null und .HasKey() – Tracker1

+0

möglich Duplikat von [dynamisch, So testen Sie, ob eine Eigenschaft verfügbar ist] (http://stackoverflow.com/questions/2998954/dynamic-how-to-test-if-a -property-is-available) – nawfal

+0

@nawfal meine war 2 Tage früher als die, mit der du verlinkt hast ... Ich dachte nur, dass es möglich sein könnte, solche Prüfmethoden mit generischen Typ-Signaturen zu erzeugen ... 'Duck.HasFunc (String-Name) 'als Beispiel-Signatur ... Ich verwende C# nicht mehr auf dieser Ebene, aber es wäre interessant. – Tracker1

Antwort

13

Versuchen Sie folgendes:

using System.Linq; 
    using System.Reflection; 
    //... 
    public dynamic Quack(dynamic duck, int i) 
    { 
     Object obj = duck as Object; 

     if (duck != null) 
     { 
      //check if object has method Quack() 
      MethodInfo method = obj.GetType().GetMethods(). 
          FirstOrDefault(x => x.Name == "Quack"); 

      //if yes 
      if (method != null) 
      { 

       //invoke and return value 
       return method.Invoke((object)duck, null); 
      } 
     } 

     return null; 
    } 

Oder diese (verwendet nur dynamisch):

public static dynamic Quack(dynamic duck) 
    { 
     try 
     { 
      //invoke and return value 
      return duck.Quack(); 
     } 
     //thrown if method call failed 
     catch (RuntimeBinderException) 
     { 
      return null; 
     }   
    } 
+6

Dies ist nicht "dynamische" überhaupt, es ist nur Standard Reflexion ... – tzaman

+3

Ich denke, das ist die halbe Lösung. Es funktioniert, wenn Ente darunter ein einfaches CLR-Objekt ist. Wenn es sich um einen dynamischen Typ handelt, der aus einer der DLR-Sprachen stammt, oder wenn es sich um ein Objekt handelt, das die IDynamicMetaObjectProvider-Schnittstelle implementiert, versucht die CLR, zuerst an diese Verknüpfung zu binden, bevor sie auf die Reflektion zurückgreift. – driis

+0

Haben Sie noch weitere Vorschläge, wie Sie das Vorhandensein einer Methode überprüfen können? – Simon

1

Der kürzeste Weg wäre es aufzurufen, und die Ausnahme zu behandeln, wenn das Verfahren nicht der Fall ist existieren. Ich komme aus Python, wo eine solche Methode in Duck-Typing üblich ist, aber ich weiß nicht, ob es in C# 4 weit verbreitet ist ...

Ich habe mich nicht getestet, da ich VC 2010 nicht habe auf meinem Rechner

dynamic Quack(dynamic duck) 
{ 
    try 
    { 
     return duck.Quack(); 
    } 
    catch (RuntimeBinderException) 
    { return null; } 
} 
13

Wenn Sie die Kontrolle über alle Objekttypen, die Sie dynamisch verwenden werden, wäre eine weitere Option sein, um sie zu zwingen, von einer Unterklasse der DynamicObject Klasse zu erben, die, wenn sie nicht scheitern zugeschnittenes Eine Methode, die nicht existiert, wird aufgerufen:

Eine schnelle und schmutzige Version würde so aussehen:

public class DynamicAnimal : DynamicObject 
{ 
    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) 
    { 
     bool success = base.TryInvokeMember(binder, args, out result); 

     // If the method didn't exist, ensure the result is null 
     if (!success) result = null; 

     // Always return true to avoid Exceptions being raised 
     return true; 
    } 
} 

Sie könnten dann wie folgt vor:

public class Duck : DynamicAnimal 
{ 
    public string Quack() 
    { 
     return "QUACK!"; 
    } 
} 

public class Cow : DynamicAnimal 
{ 
    public string Moo() 
    { 
     return "Mooooo!"; 
    } 
} 
class Program 
{ 
    static void Main(string[] args) 
    { 
     var duck = new Duck(); 
     var cow = new Cow(); 

     Console.WriteLine("Can a duck quack?"); 
     Console.WriteLine(DoQuack(duck)); 
     Console.WriteLine("Can a cow quack?"); 
     Console.WriteLine(DoQuack(cow)); 
     Console.ReadKey(); 
    } 

    public static string DoQuack(dynamic animal) 
    { 
     string result = animal.Quack(); 
     return result ?? "... silence ..."; 
    } 
} 

Und Ihre Ausgabe wäre:

Can a duck quack? 
QUACK! 
Can a cow quack? 
... silence ... 

Edit: Ich sollte anmerken, dass dies die Spitze des Eisbergs ist, wenn Sie in der Lage sind diesen Ansatz zu verwenden und auf DynamicObject aufzubauen. Sie könnten Methoden wie bool HasMember(string memberName) schreiben, wenn Sie dies wünschen.

+0

+1 für mich ... Simon's Antwort ist näher an dem, was ich will .. muss in eine HasMethod-Erweiterung-Methode zu wickeln, aber sollte in der Lage sein, es zu tun .. war nur auf mehr aus der Box hoffen. – Tracker1

3

Implementierung der HasProperty-Methode für jeden IDynamicMetaObjectProvider, OHNE dass RuntimeBinderException geworfen wird.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Dynamic; 
using Microsoft.CSharp.RuntimeBinder; 
using System.Linq.Expressions; 
using System.Runtime.CompilerServices; 


namespace DynamicCheckPropertyExistence 
{ 
    class Program 
    {   
     static void Main(string[] args) 
     { 
      dynamic testDynamicObject = new ExpandoObject(); 
      testDynamicObject.Name = "Testovaci vlastnost"; 

      Console.WriteLine(HasProperty(testDynamicObject, "Name")); 
      Console.WriteLine(HasProperty(testDynamicObject, "Id"));    
      Console.ReadLine(); 
     } 

     private static bool HasProperty(IDynamicMetaObjectProvider dynamicProvider, string name) 
     { 



      var defaultBinder = Binder.GetMember(CSharpBinderFlags.None, name, typeof(Program), 
          new[] 
            { 
             CSharpArgumentInfo.Create(
             CSharpArgumentInfoFlags.None, null) 
            }) as GetMemberBinder; 


      var callSite = CallSite<Func<CallSite, object, object>>.Create(new NoThrowGetBinderMember(name, false, defaultBinder)); 


      var result = callSite.Target(callSite, dynamicProvider); 

      if (Object.ReferenceEquals(result, NoThrowExpressionVisitor.DUMMY_RESULT)) 
      { 
       return false; 
      } 

      return true; 

     } 



    } 

    class NoThrowGetBinderMember : GetMemberBinder 
    { 
     private GetMemberBinder m_innerBinder;   

     public NoThrowGetBinderMember(string name, bool ignoreCase, GetMemberBinder innerBinder) : base(name, ignoreCase) 
     { 
      m_innerBinder = innerBinder;    
     } 

     public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion) 
     { 


      var retMetaObject = m_innerBinder.Bind(target, new DynamicMetaObject[] {});    

      var noThrowVisitor = new NoThrowExpressionVisitor(); 
      var resultExpression = noThrowVisitor.Visit(retMetaObject.Expression); 

      var finalMetaObject = new DynamicMetaObject(resultExpression, retMetaObject.Restrictions); 
      return finalMetaObject; 

     } 

    } 

    class NoThrowExpressionVisitor : ExpressionVisitor 
    {   
     public static readonly object DUMMY_RESULT = new DummyBindingResult(); 

     public NoThrowExpressionVisitor() 
     { 

     } 

     protected override Expression VisitConditional(ConditionalExpression node) 
     { 

      if (node.IfFalse.NodeType != ExpressionType.Throw) 
      { 
       return base.VisitConditional(node); 
      } 

      Expression<Func<Object>> dummyFalseResult =() => DUMMY_RESULT; 
      var invokeDummyFalseResult = Expression.Invoke(dummyFalseResult, null);          
      return Expression.Condition(node.Test, node.IfTrue, invokeDummyFalseResult); 
     } 

     private class DummyBindingResult {}  
    } 
} 
2

http://code.google.com/p/impromptu-interface/ Scheint ein schönes Interface-Mapper für dynamische Objekte zu sein ... Es ist ein bisschen mehr Arbeit ist, als ich gehofft hatte, scheint aber präsentiert die saubersten Umsetzung der Beispiele zu sein ...Ich behalte Simons Antwort für richtig, da sie immer noch am ehesten dem entspricht, was ich wollte, aber die Impromptu-Interface-Methoden sind wirklich nett.