能否调用R统计函数来优化C#函数?

6
我想知道是否有可能在C#中调用R统计优化函数(这里我想使用regnoud),而被优化的函数是用C#编写的。 我找到了RDotNet库,但是我无法从R中评估C#函数。 换句话说,问题在于R在优化时需要评估该函数,但他无法这样做,因为该函数在C#代码中。 是否有任何解决方案,例如使用.dll或其他库? 谢谢。

为什么不用R重写这个函数? - agstudy
@agstudy 只是因为这个函数(或函数集)写在了50多个类中,超过了10000行。 :( - MBS
你能分享一下其中一个函数的示例以提供更多上下文吗?你能分享一下你的不起作用的C#代码吗? - Ryan Gates
3个回答

12

我以前用了一种有点复杂的方法来做过这个,但是它有效!

首先,你需要创建一个包含你的函数的 C# DLL。您可以在 Visual Studio 中创建一个新项目时选择 "类库" 作为选项来完成这个操作。.cs 文件中的代码应该是这样的

namespace MyNamespace
{
    //expose an interface with functions to be called from R
    public interface MyInterface
    {
       string MyFunction(string name);
    }

    //create a class that implements the above interface
    public class MyClass : MyInterface
    {
      public string MyFunction(string name)
      {
         return "Hello " + name;
      }
    }

}

现在编译您的项目,您将得到一个C# DLL。这个DLL是托管DLL,不同于本地的C/C++ DLL。它不能直接从非 .Net 语言中使用,因此需要将其公开为COM对象。您可以通过以下两种方式之一来实现:

  1. 在Visual Studio中,您可以进入项目属性,在“应用程序”选项卡下单击“程序集信息”按钮,并选择“使程序集COM可见”的复选框。
  2. 或者,您可以使用在 .net 安装文件夹中找到的 regasm.exe 将 DLL 注册为 COM 组件。这里有一个描述此过程的链接。http://msdn.microsoft.com/en-us/library/tzat5yw6(v=vs.71).aspx。命令通常是“regasm myTest.dll /tlb:myTest.tlb”

COM注册过程现在已经在与您的DLL相同的文件夹中创建了一个 .tlb 文件。请保留此 .tlb 文件。我们将在下一步中需要它。

下一步是创建一个C++ DLL,该DLL可以调用COM DLL。这一步是必需的,因为R可以直接调用C++ DLL,但不能直接调用COM(如果我说错了,请纠正我,或者如果您知道更好的方法从R调用COM,则跳过此步骤)。dllmain.cpp中C++ DLL的代码如下所示(请确保滚动查看完整代码)。
#include "stdafx.h"
#include <iostream>

//import the .tlb file create by COM registration

#import "path_to_Dll\MyDll.tlb" named_guids raw_interfaces_only

    void _cdecl MyCPPFunction(char ** strName)
    {
        //Initialize COM
        HRESULT hr = CoInitialize(NULL);

        //create COM interface pointer
        MyNamespace::MyInterfacePtr myPtr;

        //create instance of COM class using the interface pointer
        HRESULT hr2 = myPtr.CreateInstance(MyNamespace::CLSID_MyClass);

       //create variable to hold output from COM.
       BSTR output;

       //call the COM function
       myPtr->MyFunction(_bstr_t(strName[0]), &output);

       //convert the returned BSTR from .net into char*
       int length = (int) SysStringLen(output);
       char *tempBuffer;
       tempBuffer = (char *) malloc(1 + length);
       WideCharToMultibyte(CP_ACP, 0, output, -1, tempBuffer, length, NULL, NULL);
       tempBuffer[length] = '\0';

       //release interface
       myPtr->Release();

       //uninitialize COM
       if(hr == S_OK)
         CoUninitialize();

       //set output in input for returning to R (this is weird but the only way I could make it work)
       strName[0] = tempBuffer;
    }

现在编译你的项目,你会得到一个C++ DLL。我们已经接近成功了!别放弃 :). 现在,你有一个作为COM对象暴露出来的C# DLL和一个可以调用该COM对象的C++ DLL。最后一步是从R中调用C++函数。以下是用于执行此操作的R代码

#load C++ DLL
dyn.load("path_to_C++_DLL")

#call function in C++ DLL and pass in a test name.
# The C++ DLL will in turn call the C# function
output <- .C("MyCPPFunction", as.character("Peter"))

#print output
print(output)

现在你应该从C#在R控制台中看到“Hello Peter”显示出来了!有一点非常重要。

始终使用“任何CPU”选项编译COM DLL。始终将C++ DLL编译为与您的R安装相匹配的版本!例如,如果您安装的是32位R,则确保将C++ DLL编译为32位DLL。如果您安装了64位R并想要使用它,请确保将C++ DLL编译为64位DLL。祝你好运!


我还没有尝试@user1建议的方法,但它看起来是一种可行的、令人印象深刻的卷积方式。在.NET世界中,如果可能的话最好避免使用COM,因为那更像是一个遗留的支撑。看看RInside,并使用user1的建议直接调用C++,而不需要经过COM的绕路。 - Dieter Menne
1
@DieterMenne - 我还有一个避免使用 COM 的方法,但它和我最初的解决方案一样复杂 :) 它涉及创建一个混合模式 DLL,这基本上是一个包含几个纯 C++ 函数和几个 Visual C++ .Net 函数的 DLL。然后,R 可以调用 C++ 函数,C++ 函数可以调用 Visual C++ 函数,Visual C++ 函数可以调用 C# DLL 而无需使用 COM。如果有人对了解更多关于 C++/CLI 感兴趣,这里有一篇详细文章的链接。http://www.codeproject.com/Articles/20466/C-CLI-Primer-Enter-the-World-of-NET-Power-Programm - user1625066

1
如果我理解您的需求正确,您有一个C#调用R再回调C#的优化问题。
我认为目前在R.NET中没有设置回调函数(“函数指针”,“委托”可能是不同人所说的),但如果我能分配时间,我可能会将此贡献给R.NET。
同时,如果可以接受这样的事情,即从R调用C#,即R是您的应用程序的入口点,那么使用rClr包肯定是可行的。我有同事在使用C#编写的模型进行优化和MCMC分析。其中tutorial是使用C#从R进行优化的非常简化但逼真的案例。

0

http://www.codeproject.com/Articles/25819/The-R-Statistical-Language-and-C-NET-Foundations

看看类似这样的东西。现在我不太了解C#,所以如果我说了什么不可能的话,请不要伤害我:

尝试将您想要优化的函数保存为C#中的一个字符,并使用此StatConnector库将其发送到R。 (我有一些有限的经验)。假设您将等式保存为'R'中的“z”,则可以使用“get(z)”作为变量之一调用您的R脚本。


1
谢谢,但那似乎是朝着相反的方向发展:从C#调用R。我感兴趣的是如何从R调用C#(或任何其他托管代码)。 - Hong Ooi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接