Looking for robust, general op_Dynamic implementation
Asked Answered
T

2

11

I've not been able to find a robust, general op_Dynamic implementation: can anyone point me to one? So far searches have only turned up toys or specific purpose implementations, but I'd like to have one on hand which, say, compares in robustness to C#'s default static dynamic implementation (i.e. handle lots / all cases, cache reflection calls) (it's been a while since I've looked at C#'s static dynamic, so forgive me if my assertions about it's abilities are false).

Thanks!

Thorny answered 20/2, 2011 at 14:41 Comment(3)
So if I understand correctly, you want an op_Dynamic implementation that does reflection calls. Which IMHO is as specific-purpose as any other op_Dynamic implementation you may have found. I don't see how "robustness" or "generality" are involved here...Intrench
Or did you mean implementing something like a binding to System.Dynamic.IDynamicMetaObjectProvider (i.e. a pluggable dynamic system)?Intrench
@Mauricio Scheffer - By general and robust, I mean an implementation which does property / field / method calls on any Object type (where Object base type is the general part, and property / field / method + caching is the robust part). In contrast to e.g. tomasp.net/blog/dynamic-sql.aspx where op_Dynamic is specially purposed for only the defined DynamicSqlDataReader type.Thorny
A
10

There is a module FSharp.Interop.Dynamic, on nuget that should robustly handle the dynamic operator using the dlr.

It has several advantages over a lot of the snippets out there.

  • Performance it uses Dynamitey for the dlr call which implements caching and is a .NET Standard Library
  • Handles methods that return void, you'll get a binding exception if you don't discard results of those.
  • The dlr handles the case of calling a delegate return by a function automatically, this will also allow you to do the same with an FSharpFunc
  • Adds an !? prefix operator to handle invoking directly dynamic objects and functions you don't have the type at runtime.

    It's open source, Apache license, you can look at the implementation and it includes unit test example cases.

Actin answered 29/7, 2011 at 12:33 Comment(2)
BTW - you might consider using Unquote, code.google.com/p/unquote, for your F# unit tests ;) (it's available as a NuGet package too). Comparison with FsUnit: #5667872Thorny
@stephen-swensen hey your framework is really cool! I've gone ahead and switched to unquote!Actin
S
8

You can never get fully general implementation of the ? operator. The operator can be implemented differently for various types where it may need to do something special depending on the type:

  • For Dictionary<T, R>, you'd want it to use the lookup function of the dictionary
  • For the SQL objects in my article you referenced, you want it to use specific SQL API
  • For unknown .NET objects, you want it to use .NET Reflection

If you're looking for an implementation that uses Reflection, then you can use one I implemented in F# binding for MonoDevelop (available on GitHub). It is reasonably complete and handles property access, method calls as well as static members. (The rest of the linked file uses it heavily to call internal members of F# compiler). It uses Reflection directly, so it is quite slow, but it is quite feature-complete.

Another alternative would be to implement the operator on top of .NET 4.0 Dynamic Language Runtime (so that it would use the same underlying API as dynamic in C# 4). I don't think there is an implementation of this somewhere out there, but here is a simple example how you can get it:

#r "Microsoft.CSharp.dll"
open System
open System.Runtime.CompilerServices
open Microsoft.CSharp.RuntimeBinder

let (?) (inst:obj) name (arg:'T) : 'R =
  // Create site (representing dynamic operation for converting result to 'R 
  let convertSite = 
    CallSite<Func<CallSite, Object, 'R>>.Create //'
      (Binder.Convert(CSharpBinderFlags.None, typeof<'R>, null)) //'
  // Create site for the method call with single argument of type 'T
  let callSite = 
    CallSite<Func<CallSite, Object, 'T, Object>>.Create //'
      (Binder.InvokeMember
        ( CSharpBinderFlags.None, name, null, null, 
          [| CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null);
             CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) |]))
  // Run the method and perform conversion
  convertSite.Target.Invoke
    (convertSite, callSite.Target.Invoke(callSite, inst, arg))

let o = box (new Random())
let a : int = o?Next(10)

This works only for instance method calls with single argument (You can find out how to do this by looking at code generated by C# compiler for dynamic invocations). I guess if you mixed the completeness (from the first one) with the approach to use DLR (in the second one), you'd get the most robust implementation you can get.

EDIT: I also posted the code to F# Snippets. Here is the version using DLR: http://fssnip.net/2U and here is the version from F# plugin (using .NET Reflection): http://fssnip.net/2V

Signature answered 20/2, 2011 at 16:49 Comment(4)
Excellent @Tomas, the MonoDevelop implementation is very close to my requirements, and the DLR example is a nice bonus!Thorny
@Stephen: I posted more polished Reflection version to www.fssnip.net (see the link I added). It contains a couple of more comments.Signature
I found this post: codebetter.com/matthewpodwysocki/2010/02/10/… which takes a DLR approach.Thorny
@Stephen - Nice! That looks like a more sophisticated version of what I wrote.Signature

© 2022 - 2024 — McMap. All rights reserved.