如何在不同的编程语言间调用函数,对于像C/C++等语言来说,互操作性是与生俱来的,但对于像Java、C#、Dart之类的具有运行时的托管型语言来说调用非托管型语言比较容易(JNI、DllLink、FFI),互相间的调用则比较苦难,以下是几种方式:

  1. 双方运行服务器然后互相调用。无论是gRPC、事件队列驱动等。优点各自运行一个微服务、或者加一台MQ,互不打扰。缺点资源代价比较高,如果是简单的需求就很麻烦。
  2. 运行时模拟。像IKVM在本运行时对另一个语言的实现。优点无依赖,缺点不可维护
  3. 将对方变成Native Code后,再调用。dotnet平台的Native AOT与JVM平台的GraalVM Native Image都能编译成可执行或共享库文件,而这时候再由对方调用就很顺畅。

我将用最少见的C#调用Native Image编译后的Java举例

  1. 安装Native Image
    https://docs.oracle.com/en/graalvm/enterprise/20/docs/reference-manual/native-image/#build-a-shared-library:~:text=Classpath%20Exception.-,Install%20Native%20Image,-Native%20Image%20can

  2. 添加类库、在Java主类中编写入口或EntryPoint

   @CEntryPoint(name = "toSM2")
    static CCharPointer toSM2(IsolateThread thread, CCharPointer cStr, int i) {
        String jStr = CTypeConversion.toJavaString(cStr);
        return SM2jar.toSM2(jStr);
    }
  1. 编译
echo 'Main-Class: Native' > manifest.txt
javac -cp 'SM2.jar' Native.class
jar cfm native.jar .\manifest.txt -C . .
native-image -cp 'SM2.jar;native.jar' --shared -o libtoSM2
  1. 讲dll或so文件放在程序生成目录供C#中调用
static void Main(string[] args)
{
	IntPtr prms = new IntPtr(0);  
	IntPtr isolate = new IntPtr(0);  
	IntPtr thread;  
	if(graal_create_isolate(IntPtr.Zero,out isolate,out thread) != 0)  
	{  
		Console.WriteLine("error");  
		return;  
	}  
  
	string c = Marshal.PtrToStringAnsi(toSM2(thread, "292813122"));  
	Console.WriteLine(c);  
  
	graal_tear_down_isolate(thread);  
}  
  
[DllImport("libtoSM2")]  
static extern IntPtr toSM2(IntPtr isolate,string str);  
  
[DllImport("libtoSM2")]  
static extern int graal_create_isolate(IntPtr prms,out IntPtr isolate,out IntPtr thread);  
  
[DllImport("libtoSM2")]  
static extern int graal_tear_down_isolate(IntPtr thread);