32

動的オブジェクトを使用したC#での簡単なダックタイピングの例が必要です。動的オブジェクトには、実行を試みる前に、探している値、プロパティ、またはメソッドの名前の単一の文字列パラメーターを持つHas​​Value / HasProperty/HasMethodメソッドが必要であるように思われます。私はtry/catchブロックを避け、可能であればより深い反射を避けようとしています。動的言語(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
}


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

5 に答える 5

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 ...

編集:このアプローチを使用して上に構築できる場合、これは氷山の一角であることに注意する必要がありますDynamicObject。必要に応じて、次のようなメソッドを作成できbool HasMember(string memberName)ます。

于 2010-06-06T23:45:18.270 に答える
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;
    }

またはこれ(動的のみを使用):

    public static dynamic Quack(dynamic duck)
    {
        try
        {
            //invoke and return value
            return duck.Quack();
        }
        //thrown if method call failed
        catch (RuntimeBinderException)
        {
            return null;
        }        
    }
于 2010-06-06T17:53:19.997 に答える
3

RuntimeBinderExceptionをスローせずに、すべてのIDynamicMetaObjectProviderにHasPropertyメソッドを実装します。

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 {}       
    }
}
于 2010-08-23T10:57:27.057 に答える
3

http://code.google.com/p/impromptu-interface/ 動的オブジェクトの優れたインターフェイスマッパーのようです...私が期待していたよりも少し手間がかかりますが、例の最もクリーンな実装のようです提示された...Simonの答えは私が望んでいたものにまだ最も近いので、正しいままにしておきますが、Impromptuインターフェイスメソッドは本当に素晴らしいです。

于 2011-09-22T18:53:45.967 に答える
1

最短パスはそれを呼び出し、メソッドが存在しない場合は例外を処理することです。私はそのような方法がダックタイピングで一般的であるPythonから来ましたが、それがC#4で広く使用されているかどうかはわかりません...

自分のマシンにVC2010がないので、自分でテストしていません

dynamic Quack(dynamic duck)
{
    try
    {
        return duck.Quack();
    }
    catch (RuntimeBinderException)
    { return null; }
}
于 2010-06-06T18:07:28.733 に答える