33

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 with a single string parameter for the name of the value, property, or method you are looking for before trying to run against it. I'm trying to avoid try/catch blocks, and deeper reflection if possible. It just seems to be a common practice for duck typing in dynamic languages (JS, Ruby, Python etc.) that is to test for a property/method before trying to use it, then falling back to a default, or throwing a controlled exception. The example below is basically what I want to accomplish.

If the methods described above don't exist, does anyone have premade extension methods for dynamic that will do this?


Example: In JavaScript I can test for a method on an object fairly easily.

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


How would I do the same 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
}
Tracker1
  • 19,103
  • 12
  • 80
  • 106
  • Just as a note to anyone looking... ExpandoObject (unsure of others) implements IDictionary so you can test with var myDynamicAsDictionary = myDyn as IDictionary; then test for null, and .HasKey() – Tracker1 Dec 16 '10 at 21:16
  • possible duplicate of [dynamic, How to test if a property is available](http://stackoverflow.com/questions/2998954/dynamic-how-to-test-if-a-property-is-available) – nawfal Jul 19 '14 at 19:48
  • 1
    @nawfal mine was 2 days earlier than the one you linked to... I was just thinking it might be possible to create such checking methods with generic type signatures... `Duck.HasFunc(string name)` as an example signature... I don't use C# at this level anymore, but it would be interesting. – Tracker1 Aug 12 '15 at 04:33
  • I see that. Sometimes it is ok to close a question which is older if the newer question has received more attention. But I see your point. – nawfal Aug 13 '15 at 07:10

6 Answers6

16

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.

Andrew Anderson
  • 3,409
  • 22
  • 25
  • +1 for me... Simon's answer is closer to what I want.. will need to wrap into a HasMethod extension method, but should be able to do it.. was just hoping for more out of the box. – Tracker1 Jun 16 '10 at 20:37
13

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;
        }        
    }
Simon
  • 9,255
  • 4
  • 37
  • 54
  • 3
    I think this is half the solution. It works if duck underneath is a plain CLR object. If it is a dynamic type coming from one of the DLR languages, or it is an object that implements the IDynamicMetaObjectProvider interface, the CLR will try to bind to that first, before resorting to reflection. – driis Jun 06 '10 at 17:55
  • Any other suggestions on how to check the existence of a method? – Simon Jun 06 '10 at 18:00
  • I know the try/catch will work, but the reflection is probably closer to what I need, maybe an extension method wrapping on dynamic, for the checks, just hard to believe there isn't something already there for this. +1 from me, still hoping for a better answer, and may just have to play with it all a bit. Wanting to avoid the overhead of a premature try/catch block though, opposed to a specific check as done in other duck-typing scenarios. – Tracker1 Jun 06 '10 at 18:37
  • Okay, marking this as the correct answer, as suggested by Andrew Anderson, will probably want to wrap this into an extension method, was just hoping for better duck typing in the dynamic by default. It's pretty common to test for a "Quack" method before calling the "Quack" over catching the exception... Anyone know what the cost of the reflection is over just catching the exception? especially if you know you will get an exception say 50% of the time? – Tracker1 Jun 16 '10 at 20:35
  • 1
    Note that the namespace `Microsoft.CSharp.RuntimeBinder` is not used by default, at least in my installation of LINQPad and Visual Studio 2010. You'll have to manually add a `using ...` for it or qualify `RuntimeBinderException` fully as in `Microsoft.CSharp.RuntimeBinder.RuntimeBinderException` for the second solutino to work. – Reb.Cabin Aug 20 '12 at 15:36
3

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 {}       
    }
}
Rene Stein
  • 31
  • 1
3

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.

Tracker1
  • 19,103
  • 12
  • 80
  • 106
1

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; }
}
CharlesB
  • 86,532
  • 28
  • 194
  • 218
0

Have not see a correct answer here, MS provides an example now with casting to a dictionary

dynamic employee = new ExpandoObject();
employee.Name = "John Smith";
employee.Age = 33;

foreach (var property in (IDictionary<String, Object>)employee)
{
    Console.WriteLine(property.Key + ": " + property.Value);
}
// This code example produces the following output:
// Name: John Smith
// Age: 33
user3218782
  • 111
  • 2
  • 7