(def o (reify IAsyncResult (get_AsyncState [_] (println "Hello") 7))) (.AsyncState o)
The question is why the .AsyncState interop call generates a missing method exception. There is also an assertion that calling AsyncState on o from C# code (by pulling in the AOT-compiled code) works okay.
Well, yes and no. Equivalent operations in C# and ClojureCLR yield the same results; in fact, they yield essentially identical IL code.
Let's start with C#. The recommended method for accessing a Var is via the methods in the clojure.clr.api.Clojure class. (See Using ClojureCLR in a C# project.) Assume the code above is the clojure.junk namespace. You would write:
// load clojure.junk IFn require = Clojure.var("clojure.core", "require"); require.invoke(Clojure.read("clojure.junk")); // access o IFn deref = Clojure.var("clojure.core", "deref"); dynamic o = deref.invoke(Clojure.var("clojure.junk", "o"));
The method Clojure.var returns an IFn. To access a non-function value, the recommended method is to deref the value, as shown.
Note that I have declared the C# variable o as dynamic. If I had declared it as Object, the only way to call AsyncState or get_AsyncState on o would be to cast it to an IAsyncResult--this is the magic sauce that makes the calls work in C# -- and in ClojureCLR. Declaring o as dynamic allows us call those methods directly -- which is what makes our life interesting.
Consider the following ways of making the calls in C#:
((IAsyncResult)o).get_AsyncState(); // 1 ((IAsyncResult)o).AsyncState; // 2 o.get_AsyncState(); // 3 o.AsyncState // 4
The calls in lines 1 and 2 work, as you would expect. What you might not know is that the compiler generates identical IL code for the calls: the call in line 2 becomes a call to the get_AsyncState method. The call in line 3 works. It turns into a dynamic callsite (System.Runtime.CompilerServices.CallSite
(.get_AsyncState ^IAsyncResult x) ; 1 (.AsyncState ^IAsyncResult x) ; 2 (.get_AsyncState x) ; 3 (.AsyncState x) ; 4
The type hints in lines 1 and 2 allow the calls to be resolved at compile-time, thus avoiding reflection. The generated IL is identical to that produced by the C# compiler. The calls in lines 3 and 4 will generate reflection warnings. In ClojureCLR, this type of reflection results in the generation of dynamic callsites, as with dynamic in C#. The binders used are ones developed for ClojureCLR, but the results are identical: the call in line 3 succeeds, and the call in line 4 fails with a missing method exception.
Moral: Use properties only with type hinting. Otherwise, use the get_XXX and set_XXX methods.