我知道C#使程序员能够在不安全的上下文中访问和使用指针。但是什么时候需要?
在什么情况下使用指针变得不可避免?
是否仅出于性能原因?
同样,为什么C#通过不安全的上下文公开此功能,并从中删除所有托管的优势?从理论上讲,可以使用指针而不损失托管环境的任何优势吗?
我知道C#使程序员能够在不安全的上下文中访问和使用指针。但是什么时候需要?
在什么情况下使用指针变得不可避免?
是否仅出于性能原因?
同样,为什么C#通过不安全的上下文公开此功能,并从中删除所有托管的优势?从理论上讲,可以使用指针而不损失托管环境的任何优势吗?
Answers:
什么时候需要?在什么情况下使用指针变得不可避免?
如果接受管理的安全解决方案的净成本是不可接受的,但是不安全解决方案的净成本是可以接受的。您可以通过从总成本中减去总收益来确定净成本或净收益。不安全的解决方案的好处是,“不要浪费时间进行不必要的运行时检查以确保正确性”;成本是(1)即使在关闭安全管理系统的情况下也必须编写安全的代码,以及(2)必须处理可能降低垃圾收集器效率的问题,因为它无法在具有非托管指针的内存周围移动。它。
或者,如果您是编写编组层的人。
是否仅出于性能原因?
出于性能以外的原因,使用托管语言中的指针似乎是不正确的。
在大多数情况下,您可以使用Marshal类中的方法来处理非托管代码的互操作。(在某些情况下,很难或不可能使用编组工具来解决互操作性问题,但我不知道。)
当然,正如我说的那样,如果您是编写Marshal班的人,那么显然您不会使用编组层来解决您的问题。在这种情况下,您需要使用指针来实现它。
为什么C#通过不安全的上下文公开此功能,并从中删除所有托管的优势?
这些管理优势带来了性能成本。例如,每次您向数组请求第十个元素时,运行时都需要进行检查以查看是否存在第十个元素,如果没有第十个元素,则抛出异常。使用指针可以消除运行时成本。
相应的开发人员成本是,如果您做错了,那么您将要处理内存损坏错误,这些错误会格式化硬盘并在一小时后使您的进程崩溃,而不是在错误时处理一个干净的异常。
从理论上讲,可以使用指针而不会失去托管环境的任何优势吗?
我认为“优势”是指诸如垃圾收集,类型安全和引用完整性之类的优势。因此,您的问题本质上是“理论上是否可以关闭安全系统,但仍然可以获得打开安全系统的好处?” 不,显然不是。如果您因为不喜欢该安全系统而关闭该安全系统,那么您将无法获得启用它的好处!
指针是与托管的垃圾收集环境固有的矛盾。
一旦您开始处理原始指针,GC将不知道发生了什么。
具体来说,它不知道对象是否可达,因为它不知道指针在哪里。
它也不能在内存中移动对象,因为那样会破坏指针。
所有这些都可以通过GC跟踪的指针解决。那就是引用。
您仅应在凌乱的高级互操作方案中或高度复杂的优化中使用指针。
如果必须要问,您可能不应该问。
interior_ptr
GC可以移动参考。使用不安全会将对象保留在GC的控制范围之外,并避免这种情况。“固定”固定对象,但让GC管理内存。
根据定义,如果您有一个指向对象地址的指针,并且GC对其进行了移动,则该指针不再有效。
关于为什么需要指针:主要原因是要使用非托管DLL,例如用C ++编写的DLL。
还要注意,当您固定变量并使用指针时,您更容易受到堆碎片的影响。
编辑
您已经谈到了托管代码与非托管代码的核心问题...内存如何释放?
您可以按照描述的那样混合代码以提高性能,只是不能使用指针跨越托管/非托管边界(即,不能在“不安全”上下文之外使用指针)。
至于它们如何清洁……您必须管理自己的记忆;使用(希望)创建/分配了指针指向的对象(通常在C ++ DLL中)CoTaskMemAlloc()
,并且您必须以相同的方式释放该内存(调用)CoTaskMemFree()
,否则会发生内存泄漏。请注意,只能CoTaskMemAlloc()
使用释放分配给的内存CoTaskMemFree()
。
另一种选择是从您的本机C ++ dll中公开一个方法,该方法使用一个指针并释放它...这使DLL可以决定如何释放内存,如果它使用其他某种方法来分配内存,则效果最佳。您使用的大多数本机dll是无法修改的第三方dll,并且它们通常没有(我已经看到)这样的函数要调用。
从此处获取释放内存的示例:
string[] array = new string[2];
array[0] = "hello";
array[1] = "world";
IntPtr ptr = test(array);
string result = Marshal.PtrToStringAuto(ptr);
Marshal.FreeCoTaskMem(ptr);
System.Console.WriteLine(result);
更多阅读材料:
C#取消分配IntPtr引用的内存 第二个答案向下介绍了不同的分配/取消分配方法
如何在C#中释放IntPtr? 加强了以与分配内存相同的方式取消分配的需要
http://msdn.microsoft.com/zh-cn/library/aa366533%28VS.85%29.aspx 有关分配和取消分配内存的各种方法的MSDN官方文档。
简而言之...您需要知道如何分配内存才能释放它。
编辑
如果我正确地理解了您的问题,那么简短的答案是肯定的,您可以将数据交给非托管指针,在不安全的上下文中使用它,并在退出不安全的上下文后使数据可用。
关键是您必须将要引用的托管对象固定在一个fixed
块上。这样可以防止您要引用的内存在unsafe
块中时被GC移动。这里涉及到许多微妙之处,例如,您不能重新分配在固定块中初始化的指针...如果您真的打算管理自己的代码,则应该阅读不安全和固定的语句。
综上所述,以您描述的方式管理自己的对象和使用指针的好处可能无法像您想像的那样为您带来很多性能提升。不这样做的原因:
HTH,
詹姆斯
GC.AddMemoryPressure
和显式通知非托管内存中的内存压力的垃圾回收GC.RemoveMemoryPressure
。您仍然必须自己释放内存,但是通过这种方式,垃圾回收器将在制定调度决策时考虑非托管内存。
在C#中显式使用指针的最常见原因:
之所以从C#中删除与指针关联的语法的原因(根据我的知识和观点-乔恩·斯凯特(Jon Skeet)会回答更好的B-),是因为在大多数情况下它是多余的。
从语言设计的角度来看,一旦通过垃圾回收器管理内存,就必须对指针的作用和不作用施加严格的约束。例如,使用指针指向对象的中间可能会给GC造成严重问题。因此,一旦限制到位,您就可以省略多余的语法并以“自动”引用结尾。
同样,在C / C ++中发现的超仁慈方法是常见的错误来源。在大多数情况下,微性能完全无关紧要,最好提供更严格的规则并约束开发人员,以减少难以发现的错误。因此,对于常见的业务应用程序,.NET和Java之类的所谓“托管”环境比假定与裸机竞争的语言更适合。
obj->Property
,obj.Property
而是可以使用。将澄清我的答案。