Это делает невозможным испльзование RAII паттерна. Паттерн очень удобный, в коде на С++ я его использую достаточно часто, а к хорошему быстро привыкаешь.
"Обычные" C# программисты используют try...finally, получают простыни кода и не видят проблемы. Чуть более продвинутые слышали про IDisposable и using. Но если класс не наследован от IDisposable - пишут try...finally, получают простыни кода и не видят проблемы.
В целом никакой особой проблемы нет...вот только хочется красоты и икибаны. Недавно на RSDN (ссылку потерял, автору спасибо) видел пример generic класса для подобной обертки. Написал по памяти.
Код вспомогательных классов:
namespace test
{
public static class DHelpers
{
public static Disposable<T> MakeDisposable<T>(this T obj, Action<T> dispose){
return new Disposable<T>(obj, dispose);
}
public static Disposable<T> MakeDisposable<T>(this T obj, Action<T> begin, Action<T> dispose){
return new Disposable<T>(obj, begin, dispose);
}
}
public class Disposable<T> : IDisposable
{
private readonly Action<T> dispose;
public readonly T @value;
public Disposable(T @value, Action<T> dispose){
this.dispose = dispose;
this.@value = @value;
}
public Disposable(T @value, Action<T> begin, Action<T> dispose){
this.dispose = dispose;
this.@value = @value;
begin(@value);
}
public Disposable(Func<T> get, Action<T> dispose){
this.@value = get();
this.dispose = dispose;
}
public void Dispose() { dispose(@value); }
}
}
Пример использования:
namespace test
{
..............
public class MadClass
{
public MadClass() { Console.WriteLine("Create"); }
public void Close() { Console.WriteLine("Close"); }
public void Action() { Console.WriteLine("Using"); }
}
internal class Program
{
private static void Main()
{
using (var mad = (new MadClass()).MakeDisposable(x => x.Close()))
{
mad.value.Action();
}
}
}
}
Благодаря closure можно оборачивать не только классы но и локальные переменные:
bool value = false;
Console.WriteLine(value);
using (value.MakeDisposable(_ => value = true, _ => value = false))
{
Console.WriteLine(value);
}
Console.WriteLine(value);
Вариант кода на Nemerle для решения той же задачи(автор - WolfHound c RSDN. в принципе по коду можно найти тему в которой это было). Что интересно - не понадобился отдельный класс. И расширения тоже не понадобились. И даже ключевое слово using как часть языка не понадобилось...Метопрограммирование...итить.
Сам макрос (примитивный, в реальном коде нужно было бы усложнить):
using Nemerle.Compiler;
public macro ScopeGuard(begin, end, body)
syntax ("scope", "(", begin, ";", end, ")", body)
{
<[
{
$begin;
try
{
$body
}
finally
{
$end;
}
}
]>
}
Использование:
using System;
using System.Console;
using Nemerle.Utility;
class MadClass
{
public this() { WriteLine("create"); }
public Close() : void { WriteLine("close"); }
}
module Program
{
Main() : void
{
scope (def x = MadClass(); x.Close())
{
WriteLine("Hi!");
}
_ = ReadKey();
}
}
Ну и под конец С++. Писать стандартную RAII обертку бессмысленно. Напишу лучше про "нестандартное" использование shared_ptr.
Итак, вот часто встречаемый вариант:
{
SomeType* p = GetSomeType();
DoSomethingWithSomeType(p);
ReleaseSomeType();
}
С использование boost::shared_ptr этот код может выглядеть вот так:
{
boost::shared_ptr<void> p(GetSomeType(),boost::bind(&ReleaseSomeType,_1));
DoSomethingWithSomeType(p.get());
}
Выглядит не очень практично? Вот реальные варианты использования (выдрано из личного кода)
раз
boost::shared_ptr<wchar_t> pszBuffer(
reinterpret_cast<wchar_t*>(*m_consolePaste.get()),
boost::bind<BOOL>(::VirtualFreeEx, ::GetCurrentProcess(), _1, NULL, MEM_RELEASE));
и два
m_hSharedEvent = boost::shared_ptr<void>(
::CreateEvent(NULL, FALSE, FALSE, (name + std::wstring(L"_event")).c_str()),
::CloseHandle);
То ли еще будет...когда в С++ появятся лямбды и замыкания.
Хотя вариант Nemerle мне кажется наиболее оптимальным с точки зрения бритвы Оккама.
Комментариев нет:
Отправить комментарий