何时使用C#/ .NET中的指针?
我知道C#让程序员能够访问,在不安全的上下文中使用指针。 但是,这是什么时候需要?
在什么情况下,使用指针变得不可避免?
仅仅是出于性能的原因?
另外,为什么C#通过不安全的上下文来暴露这个function,并从中删除所有的pipe理优势? 在理论上是否可以使用指针而不会失去托pipe环境的优点?
这是什么时候需要的? 在什么情况下使用指针变得不可避免?
当一个pipe理,安全的解决scheme的净成本是不可接受的,但不安全的解决scheme的净成本是可以接受的。 您可以通过从总成本中减去总收益来确定净成本或净收益。 一个不安全的解决scheme的好处是“没有时间浪费在不必要的运行时检查,以确保正确”; 成本是(1)即使在pipe理安全系统被closures的情况下也必须编写安全的代码,(2)必须处理可能使得垃圾收集器效率较低的代码,因为它不能在具有非托pipe指针的存储器周围移动它。
或者,如果你是编写编组层的人。
仅仅是出于性能的原因?
出于性能以外的原因,在托pipe语言中使用指针似乎是不正确的。
在绝大多数情况下,您可以使用Marshal类中的方法来处理与非托pipe代码的互操作。 (可能有一些情况是很难或不可能使用编组装置来解决互操作问题,但我不知道。)
当然,正如我所说,如果你是编写元帅类的人,那么显然你不能使用编组层来解决你的问题。 在这种情况下,你需要使用指针来实现它。
为什么C#通过一个不安全的上下文来暴露这个function,并从中删除所有的pipe理优势?
这些pipe理优势与性能成本。 例如,每次向数组提出第十个元素时,运行时都需要检查是否存在第十个元素,如果没有,则抛出exception。 使用指针可以消除运行时成本。
相应的开发人员的成本是,如果你做错了,那么你会得到处理内存损坏的错误格式化你的硬盘,并在一个小时后崩溃你的过程,而不是在错误的地方处理一个很好的干净的例外。
理论上可以使用指针而不会失去托pipe环境的任何优势吗?
通过“优势”,我认为你的意思是垃圾收集,types安全和参照完整性等优点。 因此,您的问题基本上是“理论上可以closures安全系统,但仍然可以从安全系统开启的好处中获益”。 不,显然不是。 如果你关掉这个安全系统,因为你不喜欢这个系统有多昂贵,那么你就没有得到它的好处!
指针是被pipe理的,垃圾收集的环境的内在矛盾。
一旦你开始搞乱生鱼指针,GC不知道发生了什么事情。
具体而言,它不能分辨对象是否可达,因为它不知道你的指针在哪里。
它也不能在内存中移动对象,因为这会破坏你的指针。
所有这些都可以通过GC跟踪的指针来解决。 这是什么参考。
您应该只在混乱的高级互操作场景中使用指针,或者进行高度复杂的优化。
如果你不得不问,你可能不应该。
GC可以移动引用; 使用不安全的东西会使GC控制之外的对象保持不变,并避免这种情况。 “固定”引脚对象,但让GCpipe理内存。
根据定义,如果有一个指向对象地址的指针,并且GC移动它,则指针不再有效。
至于为什么你需要指针:主要原因是使用非托pipeDLL,例如用C ++编写的DLL
还要注意,当你固定variables并使用指针时,你更容易受到碎片的影响。
编辑
你已经谈到了托pipe代码和非托pipe代码的核心问题……内存如何被释放?
你可以按照你所描述的性能混合代码,你不能用指针交叉托pipe/非托pipe的边界(即你不能在“不安全的”上下文之外使用指针)。
至于他们如何得到清洁…你必须pipe理自己的记忆, 你的指针指向的对象是使用(希望是) CoTaskMemAlloc()
创build/分配的(通常在C ++ DLL中),你必须以相同的方式释放内存,调用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/en-us/library/aa366533%28VS.85%29.aspx关于分配和释放内存的各种方法的官方MSDN文档。;
总之…你需要知道如何分配内存以释放它。
编辑如果我正确理解你的问题,简短的答案是肯定的,你可以将数据交给非托pipe指针,在不安全的上下文中处理,并在退出不安全上下文时使用这些数据。
关键是你必须用一个fixed
块fixed
你引用的被pipe理对象。 这可以防止GC在unsafe
块中移动引用的内存。 这里涉及到许多微妙之处,比如你不能重新分配一个在固定块中初始化的指针……如果你真正设置了pipe理自己的代码,你应该阅读不安全和固定的语句。
总而言之,pipe理自己的对象和按照您描述的方式使用指针的好处可能不会像您想象的那样大幅度提高性能。 原因为什么不:
- C#非常优化,速度非常快
- 你的指针代码仍然是以IL来产生的,必须被激化(在这一点上进一步的优化起作用)
- 你不会把垃圾收集器关掉……你只是把你正在使用的对象放在GC的范围之外。 所以每隔100ms左右,GC 仍然会中断你的代码,并执行你的托pipe代码中所有其他variables的函数。
HTH,
詹姆士
在C#中明确使用指针的最常见原因是:
- 做低级别的工作(如string操作),这是非常性能敏感的,
- 接口与非托pipe的API。
从C#中删除与指针相关的语法的原因(根据我的知识和观点 – Jon Skeet会回答更好的B – ))在大多数情况下,它被certificate是多余的。
从语言devise的angular度来看,一旦你通过一个垃圾收集器来pipe理内存,你必须引入严格的限制,指针是什么和什么是不可能做的。 例如,使用指向对象中间的指针可能会给GC造成严重的问题。 因此,一旦限制到位,您可以省略额外的语法,并以“自动”引用结束。
而且,在C / C ++中发现的超仁慈的方法是错误的常见来源。 在大多数情况下,微型性能根本不重要,最好是提供更严格的规则,并限制开发人员,以减less很难发现的错误。 因此,对于常见的业务应用程序,.NET和Java等所谓的“托pipe”环境比假定对裸机有效的语言更适合。
假设你想使用IPC(共享内存)在2个应用程序之间进行通信,那么你可以将数据编组到内存,并通过Windows消息传递或者其他方式将这个数据指针传递给另一个应用程序。 在接收应用程序时,您可以取回数据。
在将数据从.NET传输到传统VB6应用程序的情况下也是有用的,在这些应用程序中,您将数据编组到内存,使用win msging传递指向VB6应用程序的指针,使用VB6 copymemory()从托pipe内存空间获取数据到VB6应用程序非托pipe内存空间..