views:

67

answers:

3

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?

+1  A: 

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.

Paolo
The unmanaged library handles the state. My classes simply give it the .NET look-and-feel. I see what you're saying about deleting the objects, though. They're disposable, but then I realized that if they're being stored in a `KeyValuePair`, the objects won't get garbage collected anyway. What I'm concerned about is the overhead of having thousands of objects that all do basically the same thing. Would it even be that noticeable, or am I just trying to optimize for the sake of optimizing? C++/CLI is not an option, because I want to port this to Mono later.
David Brown
In general creating thousands of objects is going to cause your garbage collector some headaches.Another option could be to pass the pointers around and have a helper class that takes a pointer and can return the data you require. e.g. Helper.GetIdFromUnmanagedObject(myPtr) rather than have the pointer wrapper in an object with its own behaviour. Not as pretty in OO terms but at least you then only have the unmanaged objects, not the wrappers as well.
Paolo
Yeah, I basically have that already (the unmanaged API is built somewhat like that in the first place), but I'm really trying to get it blended into the framework with enumerators and such to make it as intuitive as possible. I guess I'll just wait and see how it turns out and decide on the static reference collection when it actually becomes a problem. I looked at it a little closer and it's possible that the objects will get GC'd quickly since they really don't have any referential ties to other objects (as the `IntPtrs` are what actually tie them together).
David Brown
A: 

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.

ironic
I have IDisposable implemented at the moment, so I could always remove the object from the dictionary when its disposed. The problem there is that it's left completely up to the user. If the object is stored in the dictionary, it won't get GC'd. I'd hate to make the user call Dispose() or Delete() on tens of thousands of objects. :(
David Brown
+1  A: 

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.

  1. You want to make extensive use of the UnmanagedAPI and don't want to duplicate this code in your .NET code.

  2. 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.

Larry Watanabe