Calling Clojure from .NET

醉酒当歌 提交于 2019-12-02 18:06:47

I was able to get it to work doing the following:

First I changed your code a bit, I was having trouble with the namespace and the compiler thinking the dots were directories. So I ended up with this.

(ns hello
  (:require [clojure.core])
  (:gen-class
   :methods [#^{:static true} [output [int int] int]]))

(defn output [a b]
  (+ a b))

(defn -output [a b]
  (output a b))

(defn -main []
  (println (str "(+ 5 10): " (output 5 10))))

Next I compiled it by calling:

Clojure.Compile.exe hello

This creates several files: hello.clj.dll, hello.clj.pdb, hello.exe, and hello.pdb You can execute hello.exe and it should run the -main function.

Next I created a simple C# console application. I then added the following references: Clojure.dll, hello.clj.dll, and hello.exe

Here is the code of the console app:

using System;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            hello h = new hello();
            System.Console.WriteLine(h.output(5, 9));
            System.Console.ReadLine();
        }
    }
}

As you can see, you should be able to create and use the hello class, it resides in the hello.exe assembly. I am not why the function "output" is not static, I assume it's a bug in the CLR compiler. I also had to use the 1.2.0 version of ClojureCLR as the latest was throwing assembly not found exceptions.

In order to execute the application, make sure to set the clojure.load.path environment variable to where your Clojure binaries reside.

Hope this helps.

Dax Fohl

I'd argue you should take another tack on this. All that gen-class stuff only exists in clojure as a hack to tell the compiler how to generate wrapper Java/C# classes around the native clojure reflective-dynamic variables.

I think it's better do all the "class" stuff in C# and keep your clojure code more native. Your choice. But if you want to go this way, write a wrapper like this:

using System;
using clojure.lang;

namespace ConsoleApplication {
    static class Hello {
        public static int Output(int a, int b) {
            RT.load("hello");
            var output = RT.var("code.clojure.example.hello", "output");
            return Convert.ToInt32(output.invoke(a, b));
        }
    }
}

That way your C# can look like normal C#

using System;

namespace ConsoleApplication {
    class Program {
        static void Main() {
            Console.WriteLine("3+12=" + Hello.Output(3, 12));
            Console.ReadLine();
        }
    }
}

And the clojure can look like normal clojure:

(ns code.clojure.example.hello)

(defn output [a b]
  (+ a b))

This will work whether you compile it or just leave it as a script. (RT.load("hello") will load the script hello.clj if it exists, or otherwise it'll load the hello.clj.dll assembly).

This allows your clojure to look like clojure and your C# to look like C#. Plus it eliminates the static method clojure interop compiler bug (and any other interop bugs that may exist), since you're completely circumventing the clojure interop compiler.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!