2010-06-06 20 views
31

Estoy queriendo tener un ejemplo de tipado de pato simple en C# usando objetos dinámicos. Me parece que un objeto dinámico debe tener métodos HasValue/HasProperty/HasMethod con un solo parámetro de cadena para el nombre del valor, propiedad o método que está buscando antes de intentar ejecutarlo. Estoy tratando de evitar probar/atrapar bloques, y una reflexión más profunda si es posible. Simplemente parece ser una práctica común para el tipado de patos en lenguajes dinámicos (JS, Ruby, Python, etc.) que es probar una propiedad/método antes de intentar usarlo, luego volver a caer en un valor predeterminado, o arrojar una excepción controlada . El siguiente ejemplo es básicamente lo que quiero lograr.Prueba de tipo de pato con C# 4 para objetos dinámicos

Si los métodos descritos anteriormente no existen, ¿alguien tiene métodos de extensión prefabricados para dynamic que harán esto?


Ejemplo: En JavaScript puedo probar un método sobre un objeto con bastante facilidad.

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


¿Cómo voy a hacer lo mismo 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

Al igual que una nota para todos los que buscan ... ExpandoObject (inseguro de otros) implementa IDictionary para que pueda probar con var myDynamicAsDictionary = myDyn como IDictionary ; luego prueba null, y .HasKey() – Tracker1

+0

posible duplicado de [dinámico, Cómo probar si una propiedad está disponible] (http://stackoverflow.com/questions/2998954/dynamic-how-to-test-if-a -property-is-available) – nawfal

+0

@nawfal mina fue 2 días antes que la que se enlazó a ... Estaba pensando que sería posible crear tales métodos de comprobación con las firmas de tipo genérico ... 'Duck.HasFunc (string name) 'como una firma de ejemplo ... Ya no uso C# en este nivel, pero sería interesante. – Tracker1

Respuesta

13

Prueba esto:

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

O esto (sólo utiliza dinámico):

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

Esto no está utilizando 'dynamic' en absoluto, es solo reflexión estándar ... – tzaman

+3

Creo que esta es la mitad de la solución. Funciona si el pato debajo es un objeto CLR simple. Si se trata de un tipo dinámico que proviene de uno de los lenguajes DLR, o es un objeto que implementa la interfaz IDynamicMetaObjectProvider, el CLR intentará enlazarse con ese primero, antes de recurrir a la reflexión. – driis

+0

¿Alguna otra sugerencia sobre cómo verificar la existencia de un método? – Simon

1

El camino más corto sería para invocarlo y controlar la excepción si el método no hace existe. Vengo de Python, donde ese método es común en el pato-tipado, pero no sé si se usa ampliamente en C# 4 ...

No me he probado porque no tengo VC 2010 en mi máquina

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

Si usted tiene control sobre todos los tipos de objeto que se va a utilizar de forma dinámica, otra opción sería la de obligarlos a heredar de una subclase de la clase DynamicObject que se adapte a no fallar cuando se invoca un método que no existe:

Una versión rápida y sucia se vería así:

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

A continuación, puede hacer lo siguiente:

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

Y su salida sería:

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

Editar: Debo señalar que esta es la punta del iceberg si usted es capaz para usar este enfoque y construir en DynamicObject. Puede escribir métodos como bool HasMember(string memberName) si así lo desea.

+0

+1 para mí ... la respuesta de Simon está más cerca de lo que deseo ... tendrá que ajustarse a un método de extensión HasMethod, pero debería ser capaz de hacerlo ... esperaba simplemente más de lo que esperaba. – Tracker1

3

Implementación del método HasProperty para cada IDynamicMetaObjectProvider SIN arrojar 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/ parece ser un asignador de interfaz agradable para objetos dinámicos ... Es un poco más de trabajo que yo esperaba, pero parece ser la aplicación más limpio de los ejemplos que se presentan ...Manteniendo la respuesta de Simon como correcta, ya que todavía está más cerca de lo que quería, pero los métodos de la interfaz Impromptu son realmente agradables.

Cuestiones relacionadas