您应该在 C# 代码中使用指针吗?有什么好处?是 The Man (Microsoft) 推荐的吗?
11 回答
来自“男人”本人:
在 C# 中很少需要使用指针,但在某些情况下需要它们。例如,以下情况需要使用不安全的上下文来允许指针:
- 处理磁盘上的现有结构
- 涉及带有指针的结构的高级 COM 或平台调用方案
- 性能关键代码
不鼓励在其他情况下使用不安全的上下文。
具体来说,不应该使用不安全的上下文来尝试在 C# 中编写 C 代码。
警告:
使用不安全上下文编写的代码无法验证是否安全,因此只有在代码完全受信任时才会执行。换句话说,不安全的代码不能在不受信任的环境中执行。例如,您不能直接从 Internet 运行不安全的代码。
如果你必须。
假设您需要对大型灰度图像(例如 2000x2000 像素)进行伪着色。GetPixel()
首先使用and编写“安全”版本SetPixel()
。如果这行得通,那太好了,继续前进。如果这被证明太慢,您可能需要获取构成图像的实际位(为了示例而忘记颜色矩阵)。使用不安全代码并没有什么“坏处”,但它增加了项目的复杂性,因此只应在必要时使用。
我不记得曾经这样做过——但我没有做过太多的互操作。我相信这是最常见的应用程序:调用本机代码。很少有使用指针可以优化一些代码的情况,但在我的经验中这种情况很少见。
如果它是任何指南,我认为自己在 C# 方面非常有经验,但如果我不得不做任何不安全的代码,我必须查阅规范/书籍/MSDN 来指导我。当然会有很多人对不安全的代码感到满意,但对(比如说)查询表达式不太熟悉......
我想说的主要问题是:-
- 不安全的代码是不可验证的。这意味着代码只能由用户从完全受信任的上下文中运行,因此,如果您需要用户从不完全受信任的任何地方运行代码(例如,未配置为这样的网络共享),您就是搞砸了。
- 缺乏可验证性(嗯,不确定这是否真的是一个词)也意味着您可能会弄乱程序中的内存。您可能会将整个类的错误带回您的应用程序 - 缓冲区溢出、悬空指针、yada yada yuck yuck 。更不用说可能会破坏内存中的数据结构而没有意识到指针何时变得奇怪。
- 如果您希望不安全的代码访问托管对象,则需要“固定”它们。这意味着 GC 不允许在内存中移动您的对象,因此托管堆可能会变得碎片化。这对性能有影响;因此,确定此问题是否超过了任何潜在的性能增益总是很重要的。
- 对于不习惯非托管方法的程序员来说,您的代码变得更难理解。然后,他们可能更容易因不安全代码给他们的一些“自由”而大发雷霆。
- 您可以编写非类型安全的代码;这实际上消除了一个很好的温暖模糊管理语言的许多优势。现在你可能会遇到可怕的类型安全问题。为什么要退后一步?
- 它使您的代码更丑陋。
我敢肯定还有更多可以添加到列表中;一般来说,正如其他人所说 - 避免,除非你必须通过 p/invoke 调用非托管方法,这需要一些特殊的指针 funking。即使那样,编组器也将主要阻止对它的需要。
“男人”也说除非必要,否则基本上避免。
哦,顺便说一句,关于固定在 MSDN 上的好文章。
不安全代码是 .NET CLR 完全支持的功能。好处是性能和与二进制代码的兼容性。运行时是一个沙箱,可以防止您崩溃和烧毁,但这是有代价的。在您对内存中的大块执行极其密集的操作(例如图像处理)的情况下,跳出运行时提供的正常安全性会更快。
话虽如此,我想这里的大多数人都会说“不要这样做”。绝大多数.NET 开发人员在他们的正常活动中不会遇到只能通过使用不安全代码来解决的案例。
我使用了不安全的代码来使用模拟来允许服务访问网络共享。如果您知道自己在做什么,这不是问题。
如果需要,您应该使用它们;这主要是在处理一些棘手的互操作场景时(例如,当我在 .NET 1.0 中为 DPAPI 编写托管包装器时需要它们),但偶尔可能是通过使用stackalloc
或类似来提高性能(在分析之后!)。
微软尽可能多地推荐它,因为他们是 C# 的设计者,他们决定添加在其中编写unsafe
代码的功能。您可以从关键字的选择以及使用关键字来描述您编写它的方法/类的要求中看到,它并非旨在成为事实上的实现选择。
BitConverter 不提供类似转换的重新解释。
专门将 unint 转换为 int 用于散列函数,您只关心位。
在结构上使用一些有用的、有充分理由的关于 c 或 C++ 惯用函数,您需要将它们视为已知长度的字节*,这对于散列也是最有用的。
内存结构中(非常具体)的极快二进制序列化(通过对它们的数组进行)虽然老实说,这最好通过直接使用 C++/CLI 来完成。
必须说,在许多情况下,需要指针的任务通常可以通过在 C++/CLI 中执行,然后将其作为 dll 导入到您的 c# 项目中来更好地解决。它不会改变代码是否“安全”,但它使一堆有用的函数更容易访问基于指针的结构。如果你真的想的话,它还允许你弄乱泛型类型或枚举。
大多数开发人员需要这样做的可能性确实很小。不过在你需要的时候很有用...
当然它不是“推荐的”,这就是为什么它被标记为“不安全”的原因。但不要让那把你吓跑。虽然,它应该让你看两次你的代码。也许有一种管理方法可以做到这一点?
使用不安全代码就像忘记了 .Net 框架的好处,我曾经用它们来创建像堆栈和其他东西这样的老式结构,但这只是用于学校,现在我不需要使用它们。
如果它使您的代码更短更清晰,请执行此操作。
“按照你的喜好,适当考虑拐角处的警察。” WSM