2010-06-06 11 views
31

Je souhaite avoir un exemple simple de typage de canard en C# en utilisant des objets dynamiques. Il me semble qu'un objet dynamique devrait avoir des méthodes HasValue/HasProperty/HasMethod avec un seul paramètre de chaîne pour le nom de la valeur, de la propriété ou de la méthode que vous recherchez avant de tenter de l'exécuter. J'essaie d'éviter les blocs try/catch, et d'approfondir la réflexion si possible. Cela semble être une pratique courante pour le dactylographie dans les langages dynamiques (JS, Ruby, Python, etc.) qui consiste à tester une propriété/méthode avant de l'utiliser, puis de revenir à une valeur par défaut ou de lancer une exception contrôlée. . L'exemple ci-dessous est essentiellement ce que je veux accomplir.Test de type Duck avec C# 4 pour les objets dynamiques

Si les méthodes décrites ci-dessus n'existent pas, quelqu'un a-t-il des méthodes d'extension premade pour dynamic qui vont le faire? Exemple: En JavaScript, je peux tester assez facilement une méthode sur un objet.

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


Comment pourrais-je faire la même chose en 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

Comme une note à tous ceux qui cherchent ... ExpandoObject (incertain des autres) implémente IDictionary pour que vous puissiez tester avec var myDynamicAsDictionary = myDyn comme IDictionary ; puis tester pour null, et .HasKey() – Tracker1

+0

duplication possible de [dynamique, Comment tester si une propriété est disponible] (http://stackoverflow.com/questions/2998954/dynamic-how-to-test-if-a -property-is-available) – nawfal

+0

@nawfal mine était 2 jours plus tôt que celui que vous avez lié à ... Je pensais juste qu'il pourrait être possible de créer de telles méthodes de vérification avec des signatures de type génériques ... 'Duck.HasFunc (string name) 'comme exemple de signature ... Je n'utilise plus C# à ce niveau, mais ce serait intéressant. – Tracker1

Répondre

13

Essayez ceci:

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

Ou cette (utilise uniquement dynamique):

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

Ceci n'utilise pas du tout le 'dynamic ', c'est juste la réflexion standard ... – tzaman

+3

Je pense que c'est la moitié de la solution. Cela fonctionne si le canard en dessous est un objet CLR simple. Si c'est un type dynamique provenant de l'un des langages DLR, ou si c'est un objet qui implémente l'interface IDynamicMetaObjectProvider, le CLR essayera de se lier à ce premier, avant de recourir à la réflexion. – driis

+0

D'autres suggestions sur la façon de vérifier l'existence d'une méthode? – Simon

1

Le chemin le plus court serait de l'invoquer, et gérer l'exception si la méthode ne fonctionne pas exister. Je viens de Python où une telle méthode est courante en dactylographie, mais je ne sais pas si elle est largement utilisée en C# 4 ...

Je ne me suis pas testé puisque je n'ai pas de VC 2010 sur ma machine

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

Si vous avez le contrôle sur tous les types d'objets que vous utiliserez dynamiquement, une autre option serait de les forcer à hériter d'une sous-classe de la classe DynamicObject qui est adapté à ne pas manquer quand une méthode qui n'existe pas est appelée:

Une version rapide et sale ressemblerait à ceci:

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

Vous pouvez ensuite effectuer les opérations suivantes:

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 ..."; 
    } 
} 

Et votre sortie serait:

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

Edit: Je dois dire que c'est la pointe de l'iceberg si vous êtes en mesure pour utiliser cette approche et construire sur DynamicObject. Vous pouvez écrire des méthodes comme bool HasMember(string memberName) si vous le désirez.

+0

+1 pour moi ... La réponse de Simon est plus proche de ce que je veux .. devra s'envelopper dans une méthode d'extension HasMethod, mais devrait être capable de le faire .. espérait juste pour plus de la boîte. – Tracker1

3

Implémentation de la méthode HasProperty pour chaque objet IDynamicMetaObjectProvider SANS RuntimeBinderException.

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/ semble être un mappeur d'interface agréable pour les objets dynamiques ... Il est un peu plus de travail que j'espérais, mais semble être la plus propre mise en œuvre des exemples présentés ...Garder la réponse de Simon comme correcte, car elle est toujours la plus proche de ce que je voulais, mais les méthodes d'interface Impromptu sont vraiment sympa.