为什么IntPtr不需要使用unsafe关键字?

8
当你在C#中使用像 int* 这样的指针时,你需要使用 unsafe 关键字,但当你使用 IntPtr 时,你不需要。这两者有什么区别?它们都可以指向一个地址。
垃圾回收器如何处理这两种类型?它们是否被不同地处理?如果是这样,有什么区别?如果不是,为什么需要 unsafe 关键字?
编辑:非常感谢大家到目前为止提供的答案,但我想知道的是它们在框架和垃圾收集器中如何被不同地处理,而不是 IntPtr 的MSDN定义。只需要进行一次Google搜索即可找到它。我想知道为什么IntPtr不需要 unsafe 关键字?我想了解我们可以在不使用此关键字的情况下使用它的原因。
4个回答

5
根据MSDN:

http://msdn.microsoft.com/en-gb/library/system.intptr(v=vs.100).aspx

这仅仅是“指针或句柄”的一种表示方式。
我已经阅读了一些关于GC如何处理IntPtr与其他托管类型不同的内容,但我没有找到任何文档或文章说明IntPtr的回收方式有所不同,即一旦IntPtr超出范围,就可以被GC回收。
关于为什么没有使用unsafe关键字,请阅读已接受的答案,特别是更新部分: unsafe代码对安全代码有任何影响吗? unsafe已经在IntPtr的实现中指定(请参见下面IntPtr实现中的字段声明),因此使用IntPtr的类不必将其使用标记为unsafe。否则,它会级联到可能在其实现中使用不安全代码的其他类。
除了unsafe代码不是IntPtr之外,它还包括字段private unsafe void* m_value;unsafe的,而且你没有直接使用它。
// Type: System.IntPtr
// Assembly: mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.dll

using System.Globalization;
using System.Runtime;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Security;

namespace System
{
  [ComVisible(true)]
  [__DynamicallyInvokable]
  [Serializable]
  public struct IntPtr : ISerializable
  {
    [SecurityCritical]
    private unsafe void* m_value;
    public static readonly IntPtr Zero;

    [__DynamicallyInvokable]
    public static int Size
    {
      [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries"), __DynamicallyInvokable] get
      {
        return 4;
      }
    }

    [SecuritySafeCritical]
    [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
    [ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)]
    [__DynamicallyInvokable]
    public IntPtr(int value)
    {
      this.m_value = (void*) value;
    }

    [SecuritySafeCritical]
    [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
    [ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)]
    [__DynamicallyInvokable]
    public IntPtr(long value)
    {
      this.m_value = (void*) checked ((int) value);
    }

    [ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)]
    [SecurityCritical]
    [CLSCompliant(false)]
    [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
    public IntPtr(void* value)
    {
      this.m_value = value;
    }

    [SecurityCritical]
    private IntPtr(SerializationInfo info, StreamingContext context)
    {
      long int64 = info.GetInt64("value");
      if (IntPtr.Size == 4 && (int64 > (long) int.MaxValue || int64 < (long) int.MinValue))
        throw new ArgumentException(Environment.GetResourceString("Serialization_InvalidPtrValue"));
      this.m_value = (void*) int64;
    }

    [ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)]
    [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
    public static explicit operator IntPtr(int value)
    {
      return new IntPtr(value);
    }

    [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
    [ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)]
    public static explicit operator IntPtr(long value)
    {
      return new IntPtr(value);
    }

    [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
    [ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)]
    [SecurityCritical]
    [CLSCompliant(false)]
    public static explicit operator IntPtr(void* value)
    {
      return new IntPtr(value);
    }

    [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
    [SecuritySafeCritical]
    [CLSCompliant(false)]
    public static explicit operator void*(IntPtr value)
    {
      return value.ToPointer();
    }

    [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
    [SecuritySafeCritical]
    public static explicit operator int(IntPtr value)
    {
      return (int) value.m_value;
    }

    [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
    [SecuritySafeCritical]
    public static explicit operator long(IntPtr value)
    {
      return (long) (int) value.m_value;
    }

    [SecuritySafeCritical]
    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
    [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
    public static bool operator ==(IntPtr value1, IntPtr value2)
    {
      return value1.m_value == value2.m_value;
    }

    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
    [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
    [SecuritySafeCritical]
    public static bool operator !=(IntPtr value1, IntPtr value2)
    {
      return value1.m_value != value2.m_value;
    }

    [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
    [ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)]
    public static IntPtr operator +(IntPtr pointer, int offset)
    {
      return new IntPtr(pointer.ToInt32() + offset);
    }

    [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
    [ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)]
    public static IntPtr operator -(IntPtr pointer, int offset)
    {
      return new IntPtr(pointer.ToInt32() - offset);
    }

    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
    [SecuritySafeCritical]
    internal unsafe bool IsNull()
    {
      return (IntPtr) this.m_value == IntPtr.Zero;
    }

    [SecurityCritical]
    unsafe void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
    {
      if (info == null)
        throw new ArgumentNullException("info");
      info.AddValue("value", (long) (int) this.m_value);
    }

    [SecuritySafeCritical]
    [__DynamicallyInvokable]
    public override unsafe bool Equals(object obj)
    {
      if (obj is IntPtr)
        return this.m_value == ((IntPtr) obj).m_value;
      else
        return false;
    }

    [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
    [SecuritySafeCritical]
    [__DynamicallyInvokable]
    public override unsafe int GetHashCode()
    {
      return (int) this.m_value;
    }

    [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
    [SecuritySafeCritical]
    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
    [__DynamicallyInvokable]
    public unsafe int ToInt32()
    {
      return (int) this.m_value;
    }

    [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
    [SecuritySafeCritical]
    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
    [__DynamicallyInvokable]
    public unsafe long ToInt64()
    {
      return (long) (int) this.m_value;
    }

    [SecuritySafeCritical]
    [__DynamicallyInvokable]
    public override unsafe string ToString()
    {
      return ((int) this.m_value).ToString((IFormatProvider) CultureInfo.InvariantCulture);
    }

    [SecuritySafeCritical]
    [__DynamicallyInvokable]
    public unsafe string ToString(string format)
    {
      return ((int) this.m_value).ToString(format, (IFormatProvider) CultureInfo.InvariantCulture);
    }

    [ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)]
    [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
    public static IntPtr Add(IntPtr pointer, int offset)
    {
      return pointer + offset;
    }

    [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
    [ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)]
    public static IntPtr Subtract(IntPtr pointer, int offset)
    {
      return pointer - offset;
    }

    [SecuritySafeCritical]
    [CLSCompliant(false)]
    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
    [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
    public unsafe void* ToPointer()
    {
      return this.m_value;
    }
  }
}

IntPtr是一个值类型。它不会被垃圾回收器回收或处理。它只是一个值类型的数字,大小与本机指针大小相同。 - David Jeske

1

IntPtr是一种托管类型,用于获取Windows操作系统的本机句柄。您不应将其与实际指针(如int*)混淆。

有关详细信息,请参见MSDN


IntPtr 表示 void*,与操作系统供应商无关,这也是为什么需要为其提供转换方法的原因。 - leppie
@leppie 是的,你是对的,尽管它是 void* 类型,在 Windows 中通常表示一个正数。 - bash.d

1
一个 IntPtr 实际上只是指针类型的托管表示。在不安全的上下文中,您可以自由地将任何指针类型转换为 IntPtr。本质上,IntPtr 只是围绕着一个 void* 的薄包装器(如果我没记错的话,它包含一个私有的 void* 字段)。
在与非托管代码进行交互时(通过 PInvokeMarshal 类),通常使用 IntPtr 作为非托管指针类型的就地替换,因为像指针一样,IntPtr 的大小随体系结构而变化(在 x86 系统上为 4 字节,在 x64 上为 8 字节)。

1
一个相关的问题是,为什么dllimport不需要不安全的上下文?
我怀疑IntPtr和dllimport不需要不安全的上下文的原因是为了方便VB.NET(它没有不安全)轻松访问本地API。
但是,dllimport、IntPtr及其交互肯定存在某些“不安全”因素。向dllimport入口点传递无效参数可能会导致崩溃,或更糟糕的是,悄悄地破坏内存。这意味着任何使用dllimport的代码在我看来都是“不安全”的。此外,如果该代码从安全代码中泄漏一个IntPtr到dllimport入口点,那么它实际上已经将其“不安全性”泄漏到了该安全代码中,因为安全代码可以修改IntPtr使其无效。
当我使用dllimport时,我更喜欢将指针键入为不安全结构指针,而不是IntPtr。这有两个巨大的好处。首先,它为不同类型的本机指针提供了类型检查。其次,它防止危险的非托管本机指针泄漏到“安全”代码中。

http://www.codeproject.com/script/Articles/ArticleVersion.aspx?aid=339290&av=638710

http://software.1713.n2.nabble.com/using-unsafe-struct-instead-of-IntPtr-with-PInvoke-td5861023.html


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