As a seasoned C++ programmer trying to get accustomed to .NET, there's an implementation detail in Microsoft's WeakReference "Target" property that's bugging me...
public class WeakReference : ISerializable
{
internal IntPtr m_handle;
internal bool m_IsLongReference;
...
public virtual object Target
{
[SecuritySafeCritical]
get
{
IntPtr handle = this.m_handle;
if (IntPtr.Zero == handle)
{
return null;
}
object result = GCHandle.InternalGet(handle);
if (!(this.m_handle == IntPtr.Zero))
{
return result;
}
return null;
}
[SecuritySafeCritical]
set
{
IntPtr handle = this.m_handle;
if (handle == IntPtr.Zero)
{
throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_HandleIsNotInitialized"));
}
object oldValue = GCHandle.InternalGet(handle);
handle = this.m_handle;
if (handle == IntPtr.Zero)
{
throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_HandleIsNotInitialized"));
}
GCHandle.InternalCompareExchange(handle, value, oldValue, false);
GC.KeepAlive(this);
}
}
...
}
The thing that's bugging me is this - why are they checking the validity of m_handle twice? Particularly in the 'set' method - the use of the GC.KeepAlive at the end of the method should keep the WeakReference from being garbage collected, and thus keep the handle non-zero - right?
And in the case of the 'get' - once we've actually retrieved a reference to the target via InternalGet, why bother checking the original m_handle value again? All I can think is that perhaps they're trying to guard against the WeakReference being disposed and finalized either during or after the InternalGet - but surely, couldn't it also be disposed and finalized before we get around to returning the object? I just can't come up w/ a valid explanation as to why this double-checking is necessary here...