如何避免到处创建新的包装对象?
我有各种包装 IntPtr 的类。它们不存储自己的数据(指针除外),而是使用属性和方法通过非托管库公开指针处的数据。它工作得很好,但我已经到了需要能够从其他包装器对象引用这些包装器对象的地步。例如:
public class Node {
private IntPtr _ptr;
public Node Parent {
get { return new Node(UnmanagedApi.GetParent(_ptr)); }
}
internal Node(IntPtr ptr) {
_ptr = ptr;
}
}
现在,我可以简单地返回一个 new Node(parentPtr)
(如上所述),但有可能拥有数万个节点。这不是一个坏主意吗,因为多个包装器对象最终可能会引用同一个 IntPtr
?
我可以做什么来解决这个问题?我考虑过使用静态 KeyedCollection
类,该类使用每个 IntPtr
作为键。因此,我可以直接查找它,而不是每次都返回一个新的Node
。但这会带来线程问题,对吧?
有更好的办法吗?
I have various classes that wrap an IntPtr
. They don't store their own data (other than the pointer), but instead use properties and methods to expose the data at the pointer using an unmanaged library. It works well, but I've gotten to the point where I need to be able to refer to these wrapper objects from other wrapper objects. For example:
public class Node {
private IntPtr _ptr;
public Node Parent {
get { return new Node(UnmanagedApi.GetParent(_ptr)); }
}
internal Node(IntPtr ptr) {
_ptr = ptr;
}
}
Now, I can simply return a new Node(parentPtr)
(as above), but there is the potential for having tens of thousands of nodes. Wouldn't this be a bad idea, since multiple wrapper objects could end up referring to the same IntPtr
?
What can I do to fix this? I thought about using a static KeyedCollection
class that uses each IntPtr
as the key. So, instead of returning a new Node
each time, I can just look it up. But that would bring up threading issues, right?
Is there a better way?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我能看到的最大问题是谁负责删除指针引用的对象?
重用同一对象不一定是线程问题,但如果您负责对非托管对象调用删除,则需要在对象中实现某种引用计数。
如果您的对象是只读的,则使用具有相同指针的多个对象可能会更容易。如果它们具有可以更改的状态,那么您需要了解如果多个对象持有指向该状态的指针,则进行更改的影响。
您可能还想查看 C++/CLI(托管 C++),以在 C# 和非托管库之间提供一个层,并在其中完成翻译/操作的艰苦工作,并为 C# 提供更简单的 API 来使用。
The biggest problem I can see is who is responsible for deleting the objects referred to by the pointer?
Reusing the same object is not necessarily a threading issue, although if you are responsible for calling delete on the unmanaged objects you'll need to implement some sort of reference counting in your objects.
Using multiple objects with the same pointer might be easier if your objects are read-only. If they have state that can be changed then you'll need to understand the impact of making a change if multiple objects hold a pointer to that state.
You might also want to look at C++/CLI (managed C++) to provide a layer between the C# and unmanaged library and do the hard work of translation/manipulation in there and provide a simpler API for the C# to work with.
整个代码看起来不正确。
您对 GetParent 函数的使用似乎暗示您有一个树状结构。让我对你的代码做一些猜测,它们可能是错误的。
您想要广泛使用 UnmanagementAPI,并且不想在 .NET 代码中重复此代码。
您只是想确保不会因访问未镜像的代码而导致内存问题。
我建议您不要在逐个节点的基础上创建 .NET 代码,而是为整个树/图结构创建一个 .NET 包装器,并提供一个 .NET API,该 API 可以将非托管 API 指针作为参数传递,但严格处理分配/释放,以避免内存问题。这将避免仅仅为了分配已经存在的东西(即 GetParent 函数)而不必要地分配新的内存结构。
This whole code doesn't look right.
Your use of the GetParent function seems to imply that you have a tree-like structure. Let me make a few guesses about your code, they could be wrong.
You want to make extensive use of the UnmanagedAPI and don't want to duplicate this code in your .NET code.
You simply want to make sure you don't end up with memory problems by accessing your unamaged code.
I would suggest that instead of creating .NET code on a node-by-node basis, you create a .NET wrapper for the entire tree/graph structure and provide a .NET API that may pass unmanaged API pointers as arguments, but handles strictly the allocation/deallocation so that you avoid memory problems. This will avoid the unnecessary allocation of a new memory structure simply to allocate something that already exists, i.e. the GetParent function.
我有一个相关的问题。删除非托管对象是显式完成的。所以我所做的是为所有包装器创建一个基类,其中包含可用包装器实例的静态字典。对象在构造函数中添加到字典中,并在 WrapperBase.Delete() 方法中删除。请注意,对于这种方法,具有显式的 Delete() 方法非常重要 - 否则 GC 将永远不会因为来自静态字典的引用而释放包装器实例。
I had a related issue. Deleting unmanaged objects was done explicitly. So what I did was making a base class for all wrappers that contained static dictionary for available wrappers instances. Objects were added to dictionary in constructor and deleted in WrapperBase.Delete() method. Note that it is important to have explicit Delete() method for such approach - otherwise GC will never free wrappers instances because of references from static dictionary.