代码之家  ›  专栏  ›  技术社区  ›  Tracker1

用C#4对动态物体进行鸭式试验

  •  32
  • Tracker1  · 技术社区  · 15 年前

    如果上面描述的方法不存在,是否有人为dynamic预先准备了扩展方法来实现这一点?


    示例:在JavaScript中,我可以很容易地在对象上测试方法。

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


    在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
    }
    5 回复  |  直到 15 年前
        1
  •  13
  •   Simon    15 年前

    试试这个:

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

        public static dynamic Quack(dynamic duck)
        {
            try
            {
                //invoke and return value
                return duck.Quack();
            }
            //thrown if method call failed
            catch (RuntimeBinderException)
            {
                return null;
            }        
        }
    
        2
  •  14
  •   Andrew Anderson    15 年前

    DynamicObject 类,该类定制为在调用不存在的方法时不会失败:

    快速而肮脏的版本如下所示:

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

    然后可以执行以下操作:

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

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

    动态对象 . 你可以写这样的方法 bool HasMember(string memberName)

        3
  •  3
  •   Rene Stein    15 年前

    为每个IDynamicMetaObjectProvider实现HasProperty方法,而不引发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 {}       
        }
    }
    
        4
  •  3
  •   Tracker1    14 年前

    http://code.google.com/p/impromptu-interface/ 似乎是一个很好的接口映射动态对象。。。这比我希望的要多一些,但似乎是所展示示例的最干净的实现。。。保持Simon的答案正确,因为它仍然是最接近我想要的,但是即兴的接口方法非常好。

        5
  •  1
  •   CharlesB Craig McQueen    15 年前

    最短的路径是调用它,如果方法不存在,则处理异常。我来自Python,这种方法在duck类型中很常见,但我不知道它是否在C#4中广泛使用。。。

    自从我的机器上没有VC 2010以来,我就没有测试过自己

    dynamic Quack(dynamic duck)
    {
        try
        {
            return duck.Quack();
        }
        catch (RuntimeBinderException)
        { return null; }
    }
    
    推荐文章