6

Consider the following:

using System;
using System.Dynamic;
using System.Linq;

namespace DynamicObjectTest
{
    class Program
    {
        static void Main(string[] args)
        {
            dynamic dyn = new TestDynamic();
            Console.ReadKey();

            foreach (var i in Enumerable.Range(0, 100))
            {
                Console.WriteLine(dyn.Foo());
                Console.WriteLine(dyn.Bar());
            }

            Console.ReadKey();
        }
    }

    class TestDynamic : DynamicObject
    {
        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
        {
            if (binder.Name == "Foo")
            {
                result = "Bar";
                return true;
            }
            else if (binder.Name == "Bar")
            {
                result = "Foo";
                return true;
            }

            return base.TryInvokeMember(binder, args, out result);
        }
    }
}

When running this program, four exceptions of type RuntimeBinderException are thrown. You can observe this either by breaking on all thrown exceptions (Debug/Exceptions.../check the Common Language Runtime Exceptions Throw checkbox) or by using perfmon:

enter image description here

Now these exceptions are obviously caught and handled internally because the TryInvokeMember method is called afterwards. However, it seems heavy-handed and an inappropriate use of exceptions if you subscribe to the "use exceptions only in exceptional circumstances" mantra. For my specific example it's not really a problem because the same members - Foo and Bar - are repeatedly invoked. However, I have other scenarios in mind where the members are far less static.

Is there anything that can be done to help the runtime invoke TryInvokeMember without throwing any exceptions?

Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
Kent Boogaart
  • 175,602
  • 35
  • 392
  • 393
  • 1
    There's probably an internal exceptional case that they're swallowing. You could diassemble the code for `TryInvokeMember` to see what's going on. – Polynomial Nov 16 '11 at 12:34
  • If I use the checkbox/thrown approach, it doesn't actually break for me. – Marc Gravell Nov 16 '11 at 12:42
  • 1
    @Polynomial: I pointed out that it's internal in my question, but that's not my question. – Kent Boogaart Nov 16 '11 at 12:45
  • @Marc: interesting. Do you see the exceptions in perfmon? – Kent Boogaart Nov 16 '11 at 12:45
  • Well if you squint calling foo or bar on an object without one is exceptional. Could be reuse abuse this. If they've reused teh code that would resolve static method names which does throw exceptions... Faced with the prospect of re-write or duplicating it they's picked quick but not exactky right. – Tony Hopkinson Nov 16 '11 at 13:19
  • My guess is that it first tries calling actual `Foo()` method and if that fails (throws an exception), only then it actually calls `TryInvokeMember()`. If you add `Foo()` method to your dynamic object, no exception is thrown. Why would this require throwing an exception is beyond me, though. – svick Nov 16 '11 at 13:54
  • possible duplicate of http://stackoverflow.com/questions/2954531/lots-of-first-chance-microsoft-csharp-runtimebinderexceptions-thrown-when-dealin – jbtule Nov 16 '11 at 15:05

1 Answers1

2

The system sometimes throws and catches exceptions internally. There is nothing you can do (except submit feedback to http://connect.microsoft.com/VisualStudio but there is no basis since this is unlikely to affect performance -- other overhead related to DynamicObject greatly exceeds this).

(If I had to guess, internally this might be like the memory "page fault" hardware exception: the system allows the exception to occur, then catches it and brings things together so that the future execution succeeds. This technique can be extremely efficient since it makes use of the CPU's exception features so that no conditional branching is needed in the code.)

NOTE: To try out the code in the original post, be sure to turn off the "Just My Code" option as well as configuring to break on all thrown exceptions.

Jason Kresowaty
  • 16,105
  • 9
  • 57
  • 84
  • Can you possibly provide some references to further reading about your answer? Specifically, I'd like to read about other overhead related to DynamicObject, as well as how the CPU exception features are executed. Thanks! – defines Apr 04 '13 at 12:53