使用C ++ / CLI而不是C#的情况或利弊是什么

时间:2009-12-09 15:47:33

标签: c# .net clr c++-cli

我一直在使用.NET CLR一段时间了,我选择的语言是C#。

直到最近,我还没有意识到C ++ / CLI可以生成能够运行本机代码和托管代码的“混合模式”可执行文件。

现在知道这一点,我的另一位开发人员朋友正在讨论这个属性,并试图确定这种能力何时以及如何有用。

我认为原生代码的功能比托管代码更高效,更强大,但需要额外的开发时间。

过去,我使用严格的原生C ++代码库,并使用Interop来利用我写入本机库的功能。

我可以看到不需要额外库的好处,但我很好奇使用C ++ / CLI在C#中创建的soley托管可执行文件的所有优点/缺点,或使用Interop调用的可执行文件纯粹的原生C ++库?

(旁注:Interop / PInvoke这两个术语是否可以互换,因为我不理解术语之间的区别,只是看到它们使用相同的方式。)

4 个答案:

答案 0 :(得分:13)

使用C ++ / CLI,您可以广泛地创建三种类型的对象:

  1. 托管类型。这些将编译为与等效C#基本相同的IL。这里没有表演机会。
  2. 原生类型。编译为本机代码,就像使用直接C ++一样。
  3. 混合模式类型。这些编译成托管代码,但也允许您引用本机类型。
  4. 您可能会认为(3)就像使用PInvoke代码编写C#代码来访问本机内容 - 除了为您生成所有PInvoke内容。

    当然还有更多内容,以及一些注意事项 - 但这应该可以让您了解它的用途。

    换句话说,它真的是一种粘合语言。虽然您可以在C ++ / CLI中编写完全成熟的应用程序,但将托管和本机部分分开并使用C ++ / CLI将这两个部分比使用PInvoke更清晰地连接起来更为正常。

    另一个常见用途是使用.Net库调用扩展和现有的本机C ++代码库。

    请注意您对代码进行分区,因为在将纯C ++代码透明地编译为IL时,它可能会非常微妙!

    至于你的旁注:PInvoke是一种特殊类型的Interop。 Interop也有其他形式,例如COM Interop。事实上,更准确地说,PInvoke是一组语言功能,使Interop与本机代码更容易。

答案 1 :(得分:3)

我过去曾经有效地使用过托管C ++(.NET 1.1前身为C ++ / CLI)。当您希望在托管代码中使用本机C或C ++库时,我发现它最有效。您可以使用整个Interop / PInvoke路由,这会产生一些丑陋的C#代码并经常出现编组问题,或者您可以编写一个托管C ++包装器,这是C ++ / CLI真正发挥作用的地方。

因为C ++ / CLI是托管代码,所以可以通过添加对.DLL的引用,以正常方式从C#(或者如果你倾向于使用VB.NET)调用它。没有编组,没有dllimport,没有像这样的傻瓜。只是正常的项目参考。此外,如果您的本机库是如此设计的,那么您将获得静态链接库的好处,这是一件好事(tm)。

答案 2 :(得分:2)

Phil Nash确实击中了大事。这是我不止一次打过的另一个,也是我过去使用过C ++ / CLI的主要原因:

某些应用程序通过检查某些位置的所有DLL来扩展,以便为具有特定名称的导出函数。在C#中,没有办法声明本机C风格的导出,但您可以在C ++ / CLI中。我在C ++ / CLI中创建了一个“包装器”,它导出该方法,处理C结构到托管对象的任何转换,并将调用传递给用C#编写的程序集。

答案 3 :(得分:1)

某些类型的其他语言无法使用,例如模板, const和跟踪盒装值类型的句柄。

模板专门用于编译时。泛型在运行时是专门的。虽然CLR应该缓存泛型专业化以供将来使用(因此每次使用时都会得到相同的List),但每次请求泛型专业化时仍会有性能损失。

我知道其他语言会丢弃const属性,但是在C ++代码中进行编译时检查总比没有好。

具有类似int ^的类型允许您访问托管堆目录上的内存,而无需进行不必要的拆箱。当将盒装值的跟踪句柄传递给期望跟踪句柄的函数(例如Console :: WriteLine(Object ^))时,这可以帮助提高性能。当然,早期的拳击初始化是无法避免的。在其他语言中,您可以将引用存储在Object变量中并传递它以避免取消装箱,但是您将丢失编译时类型检查。