通过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);
}
}
}