如何在C#和C ++代码之间共享常量?

我正在编写两个进程,一个使用C#和WCF,另一个使用C ++和WWSAPI。 我希望能够在一个地方定义用于两者之间通信的地址,并让C#和C ++都使用它。 这可能吗?

我最接近的是在IDL中定义常量,然后使用MIDL和TLBIMP将其转换为可由C#使用的DLL。 然而,这似乎没有暴露常数,或者至少我无法弄清楚如何使它这样做。 也许它仅限于类型定义。

还有其他建议吗?

C#和C ++有不同的常量模型。 通常情况下,常量甚至不会在生成的C ++二进制文件中发出 – 它会在大多数时间需要的地方自动替换。

而不是使用常量,创建一个返回常量的函数,您可以从C#调用P / Invoke。

从而,

#include  const double ACCELERATION_DUE_TO_GRAVITY = 9.8; int main() { std::cout << "Acceleration due to gravity is: " << ACCELERATION_DUE_TO_GRAVITY; } 

 #include  extern "C" double AccelerationDueToGravity() { return 9.8; } int main() { std::cout << "Acceleration due to gravity is: " << AccelerationDueToGravity(); } 

您应该可以从C#调用P /。

您可以创建单独的C ++ / CLI项目并在.h文件中定义所有常量。 例如,创建名为“ConstantBridge”的C ++ / CLI类库项目和名为“CSharpProgram”的C#项目:

Constants.h

 namespace Constants { const int MAGIC_NUMBER = 42; } // String literals must be defined as macros #define MAGIC_PHRASE "Hello World" // Since stirngs must be macros it's arguably more consistent // to use `define` throughout. This is for demonstration purpose. 

ConstantBridge.h

 #include "Constants.h" namespace ConstantBridge { public ref class ConstantBridge { public: // The use of the `literal` keyword is important // `static const` will not work literal int kMagicNumber = Constants::MAGIC_NUMBER; literal String ^ kMagicPhrase = MAGIC_PHRASE; };} 

CSharpProgram.cs

 Console.WriteLine(ConstantBridge.kMagicNumber); // "42" Console.WriteLine(ConstantBridge.kMagicPhrase); // "Hello World" 

现在,让“CSharpProgram”项目引用“ConstantBridge”项目。 您的其他本机C ++项目可以简单地#include "Constants.h"

只要您引用ConstantBridge项目中的literal ,就不会生成运行时依赖项。 您可以使用ILSpy或ILdasm进行validation。 C#中的const和C ++ / CLI中的literal在编译期间“逐字地”复制到调用站点

对我的用例的其他解决方案不满意,因此编写了一个稍微讨厌的解决方案,似乎更符合原始请求; 一个文件中的常量,可以内置到C#和C ++项目中 ……

  1. .cs文件中的版本信息,位于公共位置。

像这样:

 // Version.cs public static class MyAppVersion { //build public static string Number = "1.0"; public static string Phase = "Alpha"; //configuration (these are the build constants I use, substitute your own) #if BUILD_SHIPPING public static string Configuration = "Shipping"; #elif BUILD_DEVELOPMENT public static string Configuration = "Development"; #elif BUILD_DEBUG public static string Configuration = "Debug"; #else "build type not defined" #endif } 
  1. 使用添加现有项目包含在C#项目中… [添加为链接]
  2. 使用#include包含在C ++项目中(在.cpp文件中)

像这样:

 //include version information into a .cpp #define class namespace #define public #define static #define string const char* #include "..\..\Version.cs" //or to where-ever your file is ; #undef class #undef public #undef static #undef string 
  1. 在C#中引用: MyAppVersion.Number
  2. C ++中的引用: MyAppVersion::Number

当我过去不得不做这些事情的时候,我只是在构建过程中添加了一个额外的预编译步骤,它自动创建另一个文件。

由于您的常量可能在C#中的一个类中,因此您可以将其用作源文件:

 MyClass.cs: class MyClass { public const int NUM_MONTHS = 12; //COMMON public const int YEAR_BASE = 1900; //COMMON } grep '//COMMON' MyClass.cs | sed -e 's/^ *public const [az][az]*/#define/' -e 's/ *= */ /' -e 's/;.*$//' >MyClass.h grep '//COMMON' MyClass.cs | sed -e 's/ *public //' -e 's/;.*$/;/' >MyClass.hpp 

这会给你:

 MyClass.h: #define NUM_MONTHS 12 #define YEAR_BASE 1900 MyClass.hpp: const int NUM_MONTHS = 12; const int YEAR_BASE = 1900; 

现在,让Visual Studio执行该步骤并不是我知道该怎么做的事情。 你必须调查它是否可能。 UNIXy文本处理工具真的值得下载。 我在几个盒子上安装了CygWin但是,对于这种本地化的东西,你可以使用个别的GnuWin32软件包 。

你可能在PowerShell中做类似的工作,但我并不是那么精通。


现在这有点像kludge所以我可以建议一个可能更好的方式来解决你的问题。 根本不要使用常量。 将地址放入配置文件中,并在启动时读取C#和C ++代码。

这样,您可以轻松地分享价值, 并且可以配置,以防您以后想要更改它。

对于每个常量的方法的更容易的替代方案可以是包含常量作为实例属性的类。 您可以在C#中创建它,并通过COM接口将其公开给C ++。 这比P / Invoke更容易且更不容易出错,因为您不必担心所有类型和名称都正确 – 这些都是由编译器为您完成的。

注意:我没有试过这个,我只是推测它应该有效。