Why doesn't IntPtr need the unsafe keyword?

c#, garbage-collection, intptr, pointers, unmanaged

Solution

According to MSDN:

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

It is merely a representation of "a pointer or a handle."

I have been doing some reading on how the `IntPtr` is handled differently by the GC than other managed types, and I have not found any documentation or articles stating the `IntPtr` is collected any differently, i.e., as soon as the `IntPtr` goes out of scope it can be GC'd.

About why there is no use of the `unsafe` keyword read the accepted answer especially the update:

Does unsafe code have any effect on safe code?

`unsafe` has already been specified in the implementation of `IntPtr`(see the field declarations in the `IntPtr` implementation below), so the class using the `IntPtr` does not have to mark any use of `IntPtr` it uses as `unsafe` also, otherwise it would be cascaded all the way up to other classes that might use types that have unsafe code in their implementation.

Besides the `unsafe` code is not `IntPtr`, it is the field `private unsafe void* m_value;` that is `unsafe` and you are not using it directly.

// 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;
    }
  }
}

Problem

When you use a pointer like `int*` in C#, you need to use the `unsafe` keyword, but when you use an `IntPtr`, you don't. What is the difference of these? They both can point to an address. How does the garbage collector deal with these two types? Are they dealt with differently? If so, what is the difference? If not, why one needs the `unsafe` keyword? Edit: Thanks a lot for everyone's answers so far, but what I would like to know is how they are handled differently by the framework and the garbage collector, rather than the MSDN definition of `IntPtr`. It only takes one Google search to go there. What I'd like to know why IntPtr does not need the `unsafe` keyword? I would like to grasp the reason why we can use it without the keyword.

Original source

Related problems