IntPtr.Zero与null等价吗?

67

我正在尝试设置ReadFile以异步运行,并根据MSDN,需要将lpNumberOfBytesRead设为null:

"如果这是一个异步操作,则将此参数设置为 NULL 以避免可能的错误结果。"

例如,如果我有以下内容:

  [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
  public static extern bool ReadFile(
     IntPtr hFile,
     out byte[] aBuffer,
     int cbToRead,
     IntPtr cbThatWereRead,
     ref OVERLAPPED pOverlapped
  );

我这样调用它(希望第四个参数为空):

Win32API.ReadFile(readHandle, out data_read, Win32API.BUFFER_SIZE, IntPtr.Zero, ref over_lapped);

那么这是否与使用null调用它相同呢?如果不是,我应该在声明中或函数调用本身中更改什么?

我还想知道,对于hFile引用,是否应该使用SafeHandleHandleRef而不是IntPtr?我知道要确保在完成后使用CloseHandle(IntPtr)关闭句柄,但不确定是否有任何其他原因可以使用另外两个选项而不是IntPtr。我还试图避免使用不安全的代码。

编辑:事实证明,无论如何都不应将第四个参数设置为IntPtr.Zero,因为即使我异步运行,它也可能立即返回。请参见异步磁盘I/O。啊,我喜欢自相矛盾的故事。

3个回答

81

对于如您所列举的 P/Invoke 目的,您应该使用 IntPtr.Zero 替换 NULL。请注意,这与 C# 的 null 关键字不是等价的。


11

对于值类型,您无法将null赋值。引用类型可以为null,表示未引用对象实例,但是值类型始终具有一个值。

IntPtr.Zero只是代表空指针的常量值。


4
显然IntPtr就是Chuck Norris。它可以与null进行比较,但在这样的比较中永远不会为真。 - jjxtra

9

请注意,C# >= 2.0 存在一个 bug(特性??)

其中:

if (IntPtr.Zero == null)
{
    // Won't enter here
}

这段代码可以正确编译,但它永远不会进入if语句。

我在roslyn的github页面上发了一个问题,他们回答说不会修复它,因为有些项目是以警告作为错误构建的。但仍然有部分解决方案:有一个strict编译模式会生成此警告:

<Features>strict</Features>

17
抱歉之前回复晚了,但这不是一个 bug,因为 IntPtr.Zero 不等于 null,所以那个 if 语句不会生效。 - Kobunite
@Kobunite 然后尝试编写 if (IntPtr.Zero == "Hello") 并查看发生了什么。这将导致编译时错误... 问题在于没有警告表明比较是不可能的,也没有错误。代码只是被编译器删除了。请参见生成的 IL 代码 http://goo.gl/6zpPxN - xanatos
抱歉打扰了,但它编译通过是因为它有效地使用==运算符重载来隐式转换为(IntPtr?)IntPtr.Zero == (IntPtr?)null - Marc
4
@Marc 问题不在于缺少错误提示...... http://goo.gl/7o2fKM 的问题在于,如果您尝试执行 5 == null,则会收到警告,但是如果执行 IntPtr.Zero == null,则不会收到警告。有趣的是编译器知道这种等式是不可能成立的,事实上它会删除这段代码。 - xanatos
1
为什么这个可以编译通过我不知道,IntPtr是一个结构体,不应该可以与null进行比较,甚至不应该编译通过。 - jjxtra

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接