Skip to content
dmiller edited this page Dec 20, 2011 · 1 revision

ByRef parameters

Support has been added for ByRef parameters. There is no notion of an uninitialized variable in Clojure, so the distinction seen in C# between ref and out parameters is irrelevant. The special syntactic form by-ref wraps local variables in interop calls to indicate a parameter that is to be passed by reference. Upon completion of the call, the local variable will be rebound to the value ‘returned’ from the interop call.

For example, suppose you had the following class defined in C#:

public class dm.interop.C1
{
    public int m3(int x) { return x; }
    public int m3(ref int x) { x = x + 1; return x+20; }
    public string m5(string x, ref int y) { y = y + 10;  return x + y.ToString(); }
    public int m5(int x, ref int y) { y = y + 100; return x+y; }
}

The following Clojure function, passed an instance of dm.interop.C1 and an Int32 will call the overload of m3 with the by-ref argument.

(defn f3r [c n]
  (let [m (int n)]
     (.m3 c (by-ref m))
     m))

Note that it is necessary to provide the type hint via (int n). Otherwise, the only argument to match would be a ref Object. This example will use reflection and will match on a first argument of any type that has a method with signature m3(ref int). To avoid the reflection, you could type-hint the the variable c.

To get the other overload, the interop call (.m3 c m) will do the trick.

For method m5, consider

(defn f5 [c x y]
  (let [m (int y)
        v (.m5 c x (by-ref m))]
    [v m]))

Then (f5 c1 "help" 12) => ["help22" 22] and (f5 c1 15 20) => [135 120]. In other words, because the m5 call is not resolved at compile-time, reflection will pick the correct overload at run-time.

The same mechanism works for new expressions.

The syntactic form by-ref can be used at the top-level of interop calls, as shown. (by-ref can also be used in definterface, deftype and similar mechanisms. See [Defining types](Defining types).) It can only wrap a local variable. Supplying a non-local variable argument or invoking by-ref in some place other than the top-level of an interop call will cause an exception to be thrown.

params arguments

Consider the following class:

namespace dm.interop
{
  public class C6
  {
    public static int sm1(int x, params object[] ys)
    {
        return x + ys.Length;
    }
   public static int sm1(int x, params string[] ys)
    {
        int count = x;
        foreach (String y in ys)
            count += y.Length;
        return count;
    }
   public static int m2(ref int x, params object[] ys)
   {
        x += ys.Length;
        return ys.Length;
    }
  }
}

Consider calling sm1. There are overloads with params args of type object[] and of type string[]. The first overload can be invoked by

(dm.interop.C6/sm1 12 #^objects (into-array Object [1 2 3] ))

or

(dm.interop.C6/sm1 12 #^"System.Object[]" (into-array Object  [1 2 3]))

The second overload can be invoked by

(dm.interop.C6/sm1 12 #^"System.String[]" (into-array String ["abc" "de" "f"]))

or

(dm.interop.C6/sm1 12 #^"System.String[]" (into-array ["abc" "de" "f"]))

Note that when a type name is given as a string in a type tag, it must be namespace-qualified.

For the combination of by-ref and params as given by m2:

(defn c6m2 [x] 
  (let [n (int x)
        v (dm.interop.C6/m2 (by-ref n) #^objects (into-array Object [1 2 3 4]))]
    [n v]))