通过GCHandle.ToIntPtr()比较本机代码中的.NET实例

通过GCHandle.ToIntPtr()比较本机代码中的.NET实例,.net,.net,当我使用GCHandle.Alloc(o)和GCHandle.ToIntPtr()时,我会获得一个指向.NET对象的固定地址,该地址可用于以后遵从本机代码。这很好,很漂亮 但是,在以后的某个时间点,可以再次将同一对象发送到本机。我不知道以前分配的GCHandle类型,必须重新分配。这也很好 问题是我需要跟踪本机代码中的唯一实例。我无法比较从创建的GCHandle返回的System.IntPtr,因为它们不同(这并不奇怪) 在本机代码中是否有某种方法可以让我比较两种void*类型,它们分别是GCH

当我使用
GCHandle.Alloc(o)
GCHandle.ToIntPtr()
时,我会获得一个指向.NET对象的固定地址,该地址可用于以后遵从本机代码。这很好,很漂亮

但是,在以后的某个时间点,可以再次将同一对象发送到本机。我不知道以前分配的
GCHandle
类型,必须重新分配。这也很好

问题是我需要跟踪本机代码中的唯一实例。我无法比较从创建的
GCHandle
返回的
System.IntPtr
,因为它们不同(这并不奇怪)


在本机代码中是否有某种方法可以让我比较两种
void*
类型,它们分别是
GCHandle.Alloc
'd独立的?

即使对于同一个对象,
GCHandle.ToIntPtr()
也不是唯一的。遗憾的是,在内部,
GCHandle.ToIntPtr()
不会检查它使用的内部表中是否已经存在相同的对象,因此对于每个GCHandle,它都会返回一个不同的
IntPtr
。这里没有简单的解决办法

您可以在它们内部放置一个ID(因为在托管对象中没有预先构建的唯一对象标识符)。显然,您甚至可以将托管对象固定,然后使用
GCHandle.AddrOfPinnedObject()
传递其“真实”地址。这样GC就不会移动它。这仅适用于可固定对象(即.NET所有对象类型的子集)。请注意,长时间固定对象有点令人不快,因为这会使GC的工作更加困难。没有其他简单的方法

最终,被接受的解决方案是“手动”处理标识符的分配:将传递给本机代码的对象列表和标识符存储在(可能是
静态
字典
/
ConcurrentDictionary
/
ConditionalWeakTable
中。前两个将保留对其中存储的对象的强引用,以便GC不会收集它们,第三个将保留弱引用,以便GC可以在对象不再被引用时收集它们。然后创建一个使用集合的方法,然后调用本机方法

我将添加一些(完全未经测试的)代码来实现这一点(两个版本,强引用和弱引用):

//跟踪对象的强引用:它们不会被.NET释放
//在两个不同线程中添加/删除同一密钥时,线程不安全!
公共类ManagedObjectTracker,其中TKey:class
{
私有只读ConcurrentDictionary=新ConcurrentDictionary();
私有只读ConcurrentDictionary reverseDictionary=新建ConcurrentDictionary();
私人长拉斯蒂德;
//将始终返回一个句柄!
公用bool TryAdd(TKey、out IntPtr手柄)
{
if(key==null)
{
抛出新ArgumentNullException(nameof(key));
}
bool added=false;
handle=dictionary.GetOrAdd(键,x=>
{
//不保证lastId是连续的。GetOrAdd可能会丢弃某些值
//如果存在严重的并发性
添加=真;
返回(IntPtr)联锁。增量(ref lastId);
});
如有(新增)
{
反向指令[句柄]=键;
}
增加了退货;
}
公用bool TryGetKey(IntPtr句柄,out TKey)
{
返回reverseDictionary.TryGetValue(句柄,out键);
}
公用门TRYGETHADLE(TKey键,out IntPtr手柄)
{
返回dictionary.TryGetValue(key,out句柄);
}
公共bool TryRemoveByKey(TKey键,out IntPtr句柄)
{
if(dictionary.TryRemove(key,out handle))
{
反向指令.TryRemove(手柄,输出键);
返回true;
}
返回false;
}
公共bool TryRemoveByHandle(IntPtr handle,out TKey)
{
if(反向指令.TryRemove(句柄,输出键))
{
TryRemove(键,外把手);
返回true;
}
返回false;
}
}
//跟踪对象的弱引用:它们不会被.NET释放
//在两个不同线程中添加/删除同一密钥时,线程不安全!
公共类WeakManagedObjectTracker,其中TKey:class
{
私有只读条件weaktable dictionary=new ConditionalWeakTable();
//将始终返回一个句柄!
公用bool TryAdd(TKey、out IntPtr手柄)
{
if(key==null)
{
抛出新ArgumentNullException(nameof(key));
}
SelfDisposingGCHandle handle2=dictionary.GetOrCreateValue(键);
if(handle2.IntPtr==IntPtr.Zero)
{
把手2.交换(钥匙);
handle=handle2.IntPtr;
返回true;
}
handle=handle2.IntPtr;
返回false;
}
公用bool TryGetKey(IntPtr句柄,out TKey)
{
GCHandle handle2=GCHandle.FromIntPtr(句柄);
key=(TKey)handle2.Target;
返回键!=null;
}
公用门TRYGETHADLE(TKey键,out IntPtr手柄)
{
自配置GCHandle2;
if(!dictionary.TryGetValue(key,out handle2))
{
handle=IntPtr.Zero;
返回false;
}
handle=handle2.IntPtr;
返回true;
}
公共bool TryRemoveByKey(TKey键,out IntPtr句柄)
{
if(TRYGETHADLE(钥匙,外把手))
{
删除(键);
}
返回false;
}
公共bool TryRemoveByHandle(IntPtr handle,out TKey)
{
if(TryGetKey(手柄,输出键))
{
删除(键);
返回true;
}
返回false;
}
私有密封类自处置GCHandle:IDisposable
{
公共GCHandle;
公共自助
// Strong-reference of the tracked objects: they won't be freed by .NET
// Not thread safe against adding/removing the same key from two different threads!
public class ManagedObjectTracker<TKey> where TKey : class
{
    private readonly ConcurrentDictionary<TKey, IntPtr> dictionary = new ConcurrentDictionary<TKey, IntPtr>();
    private readonly ConcurrentDictionary<IntPtr, TKey> reverseDictionary = new ConcurrentDictionary<IntPtr, TKey>();
    private long lastId;

    // Will always return a handle!
    public bool TryAdd(TKey key, out IntPtr handle)
    {
        if (key == null)
        {
            throw new ArgumentNullException(nameof(key));
        }

        bool added = false;

        handle = dictionary.GetOrAdd(key, x =>
        {
            // No guarantee lastId will be contiguous. Some values could be discarded by GetOrAdd
            // if there is heavy concurrency
            added = true;
            return (IntPtr)Interlocked.Increment(ref lastId);
        });

        if (added)
        {
            reverseDictionary[handle] = key;
        }

        return added;
    }

    public bool TryGetKey(IntPtr handle, out TKey key)
    {
        return reverseDictionary.TryGetValue(handle, out key);
    }

    public bool TryGetHandle(TKey key, out IntPtr handle)
    {
        return dictionary.TryGetValue(key, out handle);
    }

    public bool TryRemoveByKey(TKey key, out IntPtr handle)
    {
        if (dictionary.TryRemove(key, out handle))
        {
            reverseDictionary.TryRemove(handle, out key);
            return true;
        }

        return false;
    }

    public bool TryRemoveByHandle(IntPtr handle, out TKey key)
    {
        if (reverseDictionary.TryRemove(handle, out key))
        {
            dictionary.TryRemove(key, out handle);
            return true;
        }

        return false;
    }
}

// Weak-reference of the tracked objects: they won't be freed by .NET
// Not thread safe against adding/removing the same key from two different threads!
public class WeakManagedObjectTracker<TKey> where TKey : class
{
    private readonly ConditionalWeakTable<TKey, SelfDisposingGCHandle> dictionary = new ConditionalWeakTable<TKey, SelfDisposingGCHandle>();

    // Will always return a handle!
    public bool TryAdd(TKey key, out IntPtr handle)
    {
        if (key == null)
        {
            throw new ArgumentNullException(nameof(key));
        }

        SelfDisposingGCHandle handle2 = dictionary.GetOrCreateValue(key);

        if (handle2.IntPtr == IntPtr.Zero)
        {
            handle2.Swap(key);
            handle = handle2.IntPtr;
            return true;
        }

        handle = handle2.IntPtr;
        return false;
    }

    public bool TryGetKey(IntPtr handle, out TKey key)
    {
        GCHandle handle2 = GCHandle.FromIntPtr(handle);

        key = (TKey)handle2.Target;

        return key != null;
    }

    public bool TryGetHandle(TKey key, out IntPtr handle)
    {
        SelfDisposingGCHandle handle2;

        if (!dictionary.TryGetValue(key, out handle2))
        {
            handle = IntPtr.Zero;
            return false;
        }

        handle = handle2.IntPtr;
        return true;
    }

    public bool TryRemoveByKey(TKey key, out IntPtr handle)
    {
        if (TryGetHandle(key, out handle))
        {
            dictionary.Remove(key);
        }

        return false;
    }

    public bool TryRemoveByHandle(IntPtr handle, out TKey key)
    {
        if (TryGetKey(handle, out key))
        {
            dictionary.Remove(key);
            return true;
        }

        return false;
    }

    private sealed class SelfDisposingGCHandle : IDisposable
    {
        public GCHandle handle;

        public SelfDisposingGCHandle()
        {

        }

        public SelfDisposingGCHandle(object value)
        {
            handle = GCHandle.Alloc(value, GCHandleType.Weak);
        }

        public IntPtr IntPtr
        {
            get
            {
                return GCHandle.ToIntPtr(handle);
            }
        }

        public GCHandle Swap(object value)
        {
            GCHandle handle2 = handle;
            handle = GCHandle.Alloc(value, GCHandleType.Weak);
            return handle2;
        }

        ~SelfDisposingGCHandle()
        {
            if (handle.IsAllocated)
            {
                handle.Free();
            }
        }

        // Questo codice viene aggiunto per implementare in modo corretto il criterio Disposable.
        public void Dispose()
        {
            if (handle.IsAllocated)
            {
                handle.Free();
            }

            GC.SuppressFinalize(this);
        }
    }
}