tags:

views:

23

answers:

1

I have a C# .NET 2.0 CF project where I'm using a number of native methods. Several of those methods revolve around HANDLE objects of some sort. I'd like to abstract out the handle management lifetime using generics like this:

public abstract class IHandleTraits
{
    public abstract IntPtr Create();
    public abstract void Release(IntPtr handle);
}

public sealed class SafeHandle<T> : IDisposable where T : IHandleTraits
{
    private bool disposed;
    private IntPtr handle_ = NativeMethods.INVALID_HANDLE_VALUE;
    public SafeHandle()
    {
        // error CS0119: 'T' is a 'type parameter', which is not valid in the given context
        handle_ = T.Create();
        if (NativeMethods.INVALID_HANDLE_VALUE == handle_)
        {
            // throw win32 exceptions
        }
    }

    ~SafeHandle()
    {
        this.Dispose(false);
    }

    public IntPtr Handle
    {
        get { return handle_; }
    }

    public void Dispose(bool disposing)
    {
        if (this.disposed)
        {
            // error CS0119: 'T' is a 'type parameter', which is not valid in the given context
            T.Release(handle_);
            handle_ = NativeMethods.INVALID_HANDLE_VALUE;
        }
        this.disposed = true;
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }
}

Now, I could define a traits structure for each handle type like this:

internal sealed class MyHandleTraits : IHandleTraits
{
    // error CS0112: A static member cannot be marked as override, virtual, or abstract
    public static override IntPtr Create()
    {
        return NativeMethods.CreateHandle();
    }

    // error CS0112: A static member cannot be marked as override, virtual, or abstract
    public static override void Release(IntPtr handle)
    {
        NativeMethods.ReleaseHandle(handle);
    }
}

And use it in the application like this:

using (SafeHandle<MyHandleTraits> MyHandle = new SafeHandle<MyHandleTraits>)
{
    // execute native methods with this handle
} 

Obviously, this has several problems:

  1. How do I define an abstract interface for static functions?
  2. When I try to use the generic, I get an error that says it's a "type parameter" which is "not valid in the given context".

What can I do to solve or work around these issues?

Thanks, PaulH

+1  A: 

Yes, can't work. You need the actual instance of a class that implements IHandleTraits. Interfaces need a class that implements a v-table. As long as you want to seal this class, you need a factory or create an instance of T.

Realistically, the object hierarchy is wrong. SafeHandle should be an abstract base class with concrete derived classes that each know how to deal with a specific handle type. That's how the desktop version of SafeHandle is implemented as well.

You can rescue your approach by giving T the new() constraint so that you can create an instance of it. Dragging around the extra reference isn't that great though:

public sealed class SafeHandle<T> : IDisposable where T : IHandleTraits, new() {
    private bool disposed;
    private IntPtr handle_ = IntPtr.Zero;
    private T handleType;
    public SafeHandle() {
        handleType = new T();                        
        handle_ = handleType.Create();
    }
    // etc...
}
Hans Passant
I rearranged the object hierarchy per your suggestion. Thanks again.
PaulH