用C#4对动态物体进行鸭型测试

Tra*_*er1 32 duck-typing dynamic c#-4.0

我想在C#中使用动态对象进行简单的鸭子打字示例.在我看来,动态对象应该具有HasValue/HasProperty/HasMethod方法,其中包含一个字符串参数,用于在尝试对其运行之前查找的值,属性或方法的名称.我试图避免尝试/捕获块,如果可能的话,更深入的反射.在动态语言(JS,Ruby,Python等)中进行鸭子输入似乎是一种常见的做法,即在尝试使用它之前测试属性/方法,然后回退到默认值或抛出受控异常.下面的例子基本上就是我想要完成的.

如果上述方法不存在,是否有人有动态的预制扩展方法来执行此操作?


示例:在JavaScript中,我可以非常轻松地测试对象上的方法.

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


我如何在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
}
Run Code Online (Sandbox Code Playgroud)

Sim*_*mon 13

试试这个:

    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;
    }
Run Code Online (Sandbox Code Playgroud)

或者这个(仅使用动态):

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

  • 这根本不是在使用`dynamic`,它只是标准的反映...... (6认同)
  • 我认为这只是解决方案的一半.如果下面的鸭子是普通的CLR对象,它就有效.如果它是来自其中一种DLR语言的动态类型,或者它是实现IDynamicMetaObjectProvider接口的对象,则CLR将在尝试反射之前尝试首先绑定到该动态类型. (3认同)

And*_*son 13

如果您可以控制将动态使用的所有对象类型,则另一个选项是强制它们从类的子类继承,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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以执行以下操作:

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 ...";
    }
}
Run Code Online (Sandbox Code Playgroud)

你的输出将是:

Can a duck quack?
QUACK!
Can a cow quack?
... silence ...
Run Code Online (Sandbox Code Playgroud)

编辑:我应该注意,如果你能够使用这种方法并继续构建,这就是冰山一角DynamicObject.你可以写出如bool HasMember(string memberName)你所愿的方法.