управление родными ручками с помощью дженериков - PullRequest
0 голосов
/ 28 сентября 2010

У меня есть проект на C # .NET 2.0 CF, в котором я использую ряд нативных методов.Некоторые из этих методов вращаются вокруг HANDLE объектов некоторого вида.Я хотел бы абстрагироваться от времени жизни управления дескрипторами, используя такие обобщения, как это:

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

Теперь я могу определить структуру признаков для каждого типа дескриптора следующим образом:

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

Ииспользуйте его в приложении следующим образом:

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

Очевидно, что это имеет несколько проблем:

  1. Как определить интерфейс abstract для static функций?
  2. Когда я пытаюсь использовать универсальный, я получаю сообщение об ошибке, которое говорит, что это «параметр типа», который «недопустим в данном контексте».

Что я могу сделать, чтобы решитьили обойти эти проблемы?

Спасибо, PaulH

1 Ответ

1 голос
/ 28 сентября 2010

Да, не может работать.Вам нужен фактический экземпляр класса, который реализует IHandleTraits.Интерфейсам нужен класс, который реализует v-таблицу.Пока вы хотите запечатать этот класс, вам нужна фабрика или создать экземпляр T.

Реально, иерархия объектов неверна.SafeHandle должен быть абстрактным базовым классом с конкретными производными классами, каждый из которых знает, как обращаться с определенным типом дескриптора.Вот так и реализована настольная версия SafeHandle.

Вы можете спасти свой подход, задав T новое ограничение (), чтобы вы могли создать его экземпляр.Но перетаскивание дополнительной ссылки не так уж и здорово:

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