根据这个问题,可以使用 C++/CLI无缝组合托管和非托管代码。我不太明白 - 托管和非托管之间不应该有编组吗?
例如,我有一个 InnerLibrary,它被编译为本机 C++ .dll,并发布了一个标头,以及从 InnerLibrary 调用代码的 C++/CLI OuterLibrary。会有编组吗?谁来实施它,成本有多大?
根据这个问题,可以使用 C++/CLI无缝组合托管和非托管代码。我不太明白 - 托管和非托管之间不应该有编组吗?
例如,我有一个 InnerLibrary,它被编译为本机 C++ .dll,并发布了一个标头,以及从 InnerLibrary 调用代码的 C++/CLI OuterLibrary。会有编组吗?谁来实施它,成本有多大?
嗯,这是一个内置在 C++/CLI 编译器中的功能,称为 C++ Interop。您可能认为涉及的黑魔法要少得多。JIT 编译器生成的机器代码与 C++ 编译器生成的机器代码完全相同。所有 .NET 值类型在 C++ 中都有直接等价物,因此不需要转换。它不会自动处理引用类型,您必须自己处理。pin_ptr<>,通常。
它真正所做的只是注入一些代码来处理从托管堆栈帧到非托管堆栈帧的转换。该代码将一个特殊的“cookie”放在堆栈上,由垃圾收集器识别。这可以防止它误入非托管框架并将非托管指针错误地识别为对象引用。该代码没有太多内容,在 Release 构建中大约需要 5 纳秒,给予或接受。
不必进行任何编组,因为 C++/CLI 能够发出直接进行调用的不安全代码。看看 Reflector 中的一些 C++/CLI 代码——它看起来与 C# 非常不同。
这是 C# 不能做的事情(至少,不是没有unsafe
关键字和一些指针 hack),也是纯模式 C++/CLI 不能做的事情(原因与 C# 相同)。
.NET 不安全代码能够直接调用非托管函数;只是除了通过 C++/CLI 之外,这种能力并不方便。
编组是将非托管数据或调用引入托管世界的过程。它只是——可以这么说——两者之间的翻译。
使用 C++/CLI,您可以混合搭配。这意味着,如果您直接使用您的库,使用 *.h 文件并使用传统的 C++ 代码,它将是非托管的并且没有封送处理。如果您使用 BCL 类或您自己的托管代码访问该数据,您将手动添加封送层,但仅在需要时。即,LPTSTR
需要将 a 转换为托管字符串才能用作一个。使用 C++/CLI,您可以跳过这一步并坚持使用传统的 C++ 代码,创建更快、更宽松的代码,但代价是不使用安全、经过检查的托管代码。
有编组涉及,但你(即程序员)必须明确地做到这一点。
如果您的 C++CLI OuterLibrary 调用有一个采用System.String
/的函数System::String^
,则 C++ 类型系统要求您在将其传递给采用 的 InnerLibrary 函数之前执行类型转换const char*
。您必须自己进行转换 - 这就是编组。
Microsoft 发布了称为C++ Support Library的东西,它提供了帮助 C++ <-> C++CLI 交互的函数。
这取决于所涉及的数据类型。
诸如int
,等内在类型double
(string
不符合条件)在本机代码和托管代码中具有相同的表示形式,不需要封送处理。内部类型的数组也以相同的方式布局(如果我们忽略元数据 .NET 存储,但这与数组内容是分开的)。
使用所有成员都是内在类型的显式布局属性的值类型也是内存布局兼容的。
如果数据存储在托管堆上的对象中,则可能需要固定(对于所有数组都是如此)。
另一方面,类类型必须来回转换/翻译。
这里有两点:
1)托管/非托管代码转换:每个转换都有其固定成本。当 C++/CLI 代码在单个程序集中编译时,编译器会尽可能地管理所有代码,以尽量减少此类转换。当从 C++/CLI 代码调用外部非托管 Dll 时,这种优化是不可能的。因此,至少在时间紧迫的部分中,尽量减少此类转换是一个好主意。在此处查看更多信息:http: //msdn.microsoft.com/en-us/magazine/dd315414.aspx,性能和互操作边界的位置
2) 参数编组。这取决于参数类型。有些参数不需要编组,例如像 int 这样的简单类型。字符串应该被编组。一些技巧,比如固定指针,可以防止简单类型的数组编组。