Test di tipo anatra con C # 4 per oggetti dinamici

Sto volendo avere un semplice esempio di digitazione anatra in C # usando oggetti dinamici. A mio parere, un object dinamico dovrebbe avere metodi HasValue / HasProperty / HasMethod con un parametro a stringa singola per il nome del valore, della proprietà o del metodo che si sta cercando prima di provare a eseguirlo. Sto cercando di evitare i blocchi try / catch e il riflesso più profondo se ansible. Sembra essere una pratica comune per la digitazione anatra in linguaggi dinamici (JS, Ruby, Python ecc.) Che è quello di testare una proprietà / metodo prima di provare a usarlo, quindi tornare a un valore predefinito o lanciare un’eccezione controllata . L’esempio qui sotto è fondamentalmente ciò che voglio realizzare.

Se i metodi sopra descritti non esistono, qualcuno ha metodi di estensione premade per la dynamic che lo faranno?

Esempio: in JavaScript posso testare un metodo su un object abbastanza facilmente.

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

Come farei lo stesso 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 } 

Prova questo:

  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 questo (usa solo dynamic):

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

Se hai il controllo su tutti i tipi di oggetti che utilizzerai in modo dinamico, un’altra opzione sarebbe quella di forzarli ad ereditare da una sottoclass della class DynamicObject che è su misura per non fallire quando viene invocato un metodo che non esiste:

Una versione veloce e sporca sarebbe simile a questa:

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

Potresti quindi fare quanto segue:

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

E il tuo risultato sarebbe:

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

Edit: Devo notare che questa è la punta dell’iceberg se si è in grado di utilizzare questo approccio e creare DynamicObject . Potresti scrivere metodi come bool HasMember(string memberName) se lo desideri.

Implementazione del metodo HasProperty per ogni IDynamicMetaObjectProvider SENZA lanciare 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>.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> dummyFalseResult = () => DUMMY_RESULT; var invokeDummyFalseResult = Expression.Invoke(dummyFalseResult, null); return Expression.Condition(node.Test, node.IfTrue, invokeDummyFalseResult); } private class DummyBindingResult {} } } 

http://code.google.com/p/impromptu-interface/ Sembra essere un simpatico mappatore di interfacce per oggetti dinamici … È un po ‘più di lavoro di quanto speravo, ma sembra essere l’implementazione più pulita degli esempi presentato … Mantenere la risposta di Simon come corretta, dal momento che è ancora il più vicino a quello che volevo, ma i metodi dell’interfaccia Impromptu sono davvero belli.

Il percorso più breve sarebbe invocarlo e gestire l’eccezione se il metodo non esiste. Vengo da Python dove tale metodo è comune nella digitazione anatra, ma non so se è ampiamente usato in C # 4 …

Non ho provato me stesso dal momento che non ho VC 2010 sulla mia macchina

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