Duck type testing with C# 4 for dynamic objects

前端 未结 5 456
遇见更好的自我
遇见更好的自我 2020-12-02 22:57

I\'m wanting to have a simple duck typing example in C# using dynamic objects. It would seem to me, that a dynamic object should have HasValue/HasProperty/HasMethod methods

相关标签:
5条回答
  • 2020-12-02 23:26

    The shortest path would be to invoke it, and handle the exception if the method does not exist. I come from Python where such method is common in duck-typing, but I don't know if it is widely used in C#4...

    I haven't tested myself since I don't have VC 2010 on my machine

    dynamic Quack(dynamic duck)
    {
        try
        {
            return duck.Quack();
        }
        catch (RuntimeBinderException)
        { return null; }
    }
    
    0 讨论(0)
  • 2020-12-02 23:28

    Try this:

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

    Or this (uses only dynamic):

        public static dynamic Quack(dynamic duck)
        {
            try
            {
                //invoke and return value
                return duck.Quack();
            }
            //thrown if method call failed
            catch (RuntimeBinderException)
            {
                return null;
            }        
        }
    
    0 讨论(0)
  • 2020-12-02 23:41

    Implementation of the HasProperty method for every IDynamicMetaObjectProvider WITHOUT throwing 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 {}       
        }
    }
    
    0 讨论(0)
  • 2020-12-02 23:44

    http://code.google.com/p/impromptu-interface/ Seems to be a nice Interface mapper for dynamic objects... It's a bit more work than I was hoping for, but seems to be the cleanest implementation of the examples presented... Keeping Simon's answer as correct, since it is still the closest to what I wanted, but the Impromptu interface methods are really nice.

    0 讨论(0)
  • 2020-12-02 23:45

    If you have control over all of the object types that you will be using dynamically, another option would be to force them to inherit from a subclass of the DynamicObject class that is tailored to not fail when a method that does not exist is invoked:

    A quick and dirty version would look like this:

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

    You could then do the following:

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

    And your output would be:

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

    Edit: I should note that this is the tip of the iceberg if you are able to use this approach and build on DynamicObject. You could write methods like bool HasMember(string memberName) if you so desired.

    0 讨论(0)
提交回复
热议问题