Правильная реализация Dispose вместе с SafeHandleZeroOrMinusOneIsInvalid

188
06 ноября 2018, 07:30

В сети есть много примеров реализации паттерна Dispose применительно к оберткам для C++ DLL, но все они немного отличаются, такое ощущение, что многие просто перепечатывают паттерн из любимого источника и все.

Есть DLL, из которой вызывается одна единственная функция, код следующий:

public class U2KApi:IDisposable
{
    private const string FunctionName = "fname";
    private const string B5 = "param1";
    private const int B6 = param2;
    public delegate int MainFuncDelegate(int b1, int b2, int b3, int b4, string b5, int b6);
    private bool _disposedValue;
    private SafeLibraryHandle _safeLibraryHandle;
    private MainFuncDelegate _mainFuncDelegate;
    public U2KApi()
    {
        var fileName = "dllname.dll";
        _safeLibraryHandle = Kernel32Dll.LoadLibrary(fileName);
        if (_safeLibraryHandle.IsInvalid)
        {
            var hrForLastWin32Error = Marshal.GetHRForLastWin32Error();
            Marshal.ThrowExceptionForHR(hrForLastWin32Error);
        }
        var procAddress = Kernel32Dll.GetProcAddress(_safeLibraryHandle, FunctionName);
        if (procAddress == IntPtr.Zero)
        {
            _safeLibraryHandle.Close();
            throw new ArgumentException();
        }
        var delegateForFunctionPointer = Marshal.GetDelegateForFunctionPointer<MainFuncDelegate>(procAddress);
        _mainFuncDelegate = delegateForFunctionPointer;
    }
    public int MainFunc(int b1, int b2, int b3, int b4)
    {
        return _mainFuncDelegate(b1, b2, b3, b4, B5, B6);
    }
    private void Dispose(bool disposing)
    {
        if (_disposedValue) return;
        if (disposing)
        {
            if (_safeLibraryHandle != null)
            {
                if (!_safeLibraryHandle.IsClosed)
                {
                    _safeLibraryHandle.Close();
                }
            }
        }
        _safeLibraryHandle = null;
        _mainFuncDelegate = null;
        _disposedValue = true;
    }
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    ~U2KApi()
    {
        Dispose(false);
    }
}
static class Kernel32Dll
{
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern SafeLibraryHandle LoadLibrary(string fileName);
    [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
    public static extern IntPtr GetProcAddress(SafeLibraryHandle hModule, string procname);
    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool FreeLibrary(IntPtr hModule);
}
class SafeLibraryHandle : SafeHandleZeroOrMinusOneIsInvalid
{
    private SafeLibraryHandle() : base(true)
    {
    }
    protected override bool ReleaseHandle()
    {
        return Kernel32Dll.FreeLibrary(handle);
    }
}

Вопросы примерно следующие:

  • Нужно ли закрывать SafeHandle внутри if (disposing) - видел примеры обратного, когда проверка на disposing опускалась, и, честно, не понимаю, как правильно. Зачем тогда городить огород из Dispose(bool)?
  • Нужно ли присваивать null _safeLibraryHandle и _mainFuncDelegate - памяти много не съедят же?
  • Если нет, можно ли всё упростить?

UPD. Почитал ответы VladD, пытался думать. Насколько я понял, так как handle обернут в SafeHandleZeroOrMinusOneIsInvalid, то всё можно свести к простому освобождению _safeLibraryHandle в обычном Dispose(), выкинув финализатор и перегруженный Dispose(bool).

Answer 1

Когда вы используете SafeHandle, классический паттерн Dispose помножается на ноль, и вне наследников SafeHandle все методы Dispose должны быть простыми методами, которые вызывают Dispose на приватных членах, реализующих IDisposable. Никаких финализаторов в коде быть не должно.

С точки зрения инкапсуляции ваш код для загрузки библиотеки и проверки ошибок я бы переместил в SafeLibraryHandle. По возможности весь грязный интероп лучше держать в одном месте.

READ ALSO
wp_get_post_tags не получает id поста

wp_get_post_tags не получает id поста

Не могу понять почему wp_get_post_tags не получает ID поста

194
Почему не работает бот через curl method post?

Почему не работает бот через curl method post?

тренируюсь писать бота, хочу написать бота который будет авторизоваться в Twittercom и показывать ленту новостей

187
Странное поведения php и Mysql

Странное поведения php и Mysql

Мое приложения отправляет запрос на создание публикации на мой api (php and database mysql)В api использую ORM систему RedBeanPhp для запросов в базу данных

160