托管类型 (C++/CLI)
Visual C++ 允许通过托管类型访问 .NET 功能,这些类型为公共语言运行时的功能提供支持,并且受制于运行时的优点和限制。
托管类型和 main 函数
使用 /clr
编写应用程序时,main()
函数的参数不能为托管类型。
正确的签名示例为:
// managed_types_and_main.cpp
// compile with: /clr
int main(int, char*[], char*[]) {}
对应于 C++ 本机类型的 .NET Framework 类型
下表显示内置 Visual C++ 类型的关键字,即 System 命名空间中预定义类型的别名。
Visual C++ 类型 | .NET Framework 类型 |
---|---|
void |
System.Void |
bool |
System.Boolean |
signed char |
System.SByte |
unsigned char |
System.Byte |
wchar_t |
System.Char |
short 和 signed short |
System.Int16 |
unsigned short |
System.UInt16 |
int 、signed int 、long 和 signed long |
System.Int32 |
unsigned int 和 unsigned long |
System.UInt32 |
__int64 和 signed __int64 |
System.Int64 |
unsigned __int64 |
System.UInt64 |
float |
System.Single |
double 和 long double |
System.Double |
有关默认为 signed char
或 unsigned char
的编译器选项的详细信息,请参阅 /J
(默认 char
类型为 unsigned
)。
嵌套在本机类型中的值类型的版本问题
请考虑用于生成客户端程序集的已签名(强名称)程序集组件。 该组件包含一个值类型,在客户端中作为本机联合、类或数组的成员的类型。 如果组件的未来版本更改值类型的大小或布局,则必须重新编译客户端。
使用 sn.exe (sn -k mykey.snk
) 创建密钥文件。
示例
以下示例是组件。
// nested_value_types.cpp
// compile with: /clr /LD
using namespace System::Reflection;
[assembly:AssemblyVersion("1.0.0.*"),
assembly:AssemblyKeyFile("mykey.snk")];
public value struct S {
int i;
void Test() {
System::Console::WriteLine("S.i = {0}", i);
}
};
此示例是客户端:
// nested_value_types_2.cpp
// compile with: /clr
#using <nested_value_types.dll>
struct S2 {
S MyS1, MyS2;
};
int main() {
S2 MyS2a, MyS2b;
MyS2a.MyS1.i = 5;
MyS2a.MyS2.i = 6;
MyS2b.MyS1.i = 10;
MyS2b.MyS2.i = 11;
MyS2a.MyS1.Test();
MyS2a.MyS2.Test();
MyS2b.MyS1.Test();
MyS2b.MyS2.Test();
}
此示例产生以下输出:
S.i = 5
S.i = 6
S.i = 10
S.i = 11
注释
但是,如果将另一个成员添加到 nested_value_types.cpp
中的 struct S
(例如,double d;
)并重新编译组件而不重新编译客户端,则结果是未经处理的异常(System.IO.FileLoadException 类型)。
如何测试相等性
在下面的示例中,使用 Managed Extensions for C++ 的相等性测试基于句柄引用的内容。
示例
// mcppv2_equality_test.cpp
// compile with: /clr /LD
using namespace System;
bool Test1() {
String ^ str1 = "test";
String ^ str2 = "test";
return (str1 == str2);
}
此程序的 IL 显示返回值是通过调用 op_Equality
实现的。
IL_0012: call bool [mscorlib]System.String::op_Equality(string, string)
如何诊断和修复程序集兼容性问题
当在编译时引用的程序集版本与运行时引用的程序集版本不匹配时,可能会出现各种问题。
编译程序集时,可以使用 #using
语法引用其他程序集。 在编译期间,编译器会访问这些程序集。 这些程序集中的信息用于做出优化决策。
但是,如果引用的程序集已更改并重新编译,则还要重新编译依赖于它的引用程序集。 否则,程序集可能变得不兼容。 对于新的程序集版本,最初有效的优化决策可能不正确。 由于这些不兼容性,可能会发生各种运行时错误。 在这种情况下,不会生成任何特定异常。 在运行时报告失败的方式取决于导致问题的代码更改的性质。
只要为产品的已发布版本重新生成整个应用程序,这些错误在最终的生产代码中就不应该是问题。 向公众发布的程序集应标有正式版本号,这将确保避免这些问题。 有关详细信息,请参阅程序集版本控制。
诊断和修复不兼容性错误
在引用其他程序集的代码中,可能会遇到运行时异常或其他错误条件。 如果无法确定其他原因,问题可能是程序集过期。
首先,隔离并重现异常或其他错误条件。 由于过时异常而发生的问题应可重现。
检查应用程序中引用的任何程序集的时间戳。
如果任何引用程序集的时间戳晚于应用程序上次编译的时间戳,则应用程序已过期。 如果已过期,请使用最新的程序集重新编译应用程序,并在必要时编辑代码。
重新运行应用程序,执行重现问题的步骤,并验证是否未发生异常。
示例
以下程序演示了问题:它首先降低了方法的可访问性,然后尝试在不重新编译的情况下在另一个程序集中访问该方法。 首先编译 changeaccess.cpp
。 它是将更改的引用程序集。 然后编译 referencing.cpp
。 它应成功编译。 接下来,降低调用方法的可访问性。 使用编译器选项 /DCHANGE_ACCESS
重新编译 changeaccess.cpp
。 它使 access_me
方法处于 protected
状态,而不是 public
,因此不能从 Test
或其导数以外调用。 如果不重新编译 referencing.exe
,请重新运行应用程序。 将出现 MethodAccessException。
// changeaccess.cpp
// compile with: /clr:safe /LD
// After the initial compilation, add /DCHANGE_ACCESS and rerun
// referencing.exe to introduce an error at runtime. To correct
// the problem, recompile referencing.exe
public ref class Test {
#if defined(CHANGE_ACCESS)
protected:
#else
public:
#endif
int access_me() {
return 0;
}
};
下面是引用程序集的源:
// referencing.cpp
// compile with: /clr:safe
#using <changeaccess.dll>
// Force the function to be inline, to override the compiler's own
// algorithm.
__forceinline
int CallMethod(Test^ t) {
// The call is allowed only if access_me is declared public
return t->access_me();
}
int main() {
Test^ t = gcnew Test();
try
{
CallMethod(t);
System::Console::WriteLine("No exception.");
}
catch (System::Exception ^ e)
{
System::Console::WriteLine("Exception!");
}
return 0;
}
另请参阅
使用 C++/CLI (Visual C++) 进行 .NET 编程
与其他 .NET 语言的互操作性 (C++/CLI)
托管类型 (C++/CLI)
#using
指令