Вопрос

Будучи в первую очередь разработчиком на C ++, отсутствие RAII (получение ресурсов является инициализацией) в Java и .NET всегда беспокоил меня. Тот факт, что ответственность за очистку переходит от автора класса к его потребителю (с помощью try finally или с использованием конструкции ), по-видимому, заметно уступает.

Я понимаю, почему в Java нет поддержки RAII, поскольку все объекты расположены в куче, а сборщик мусора по своей сути не поддерживает детерминированное уничтожение, но в .NET с введением типов-значений ( struct ) у нас (казалось бы) идеальный кандидат на RAII. Тип значения, созданный в стеке, имеет четко определенную область видимости, и можно использовать семантику деструктора C ++. Однако CLR не позволяет типу значения иметь деструктор.

В ходе моих случайных поисков был обнаружен один аргумент: если значение типа в штучной упаковке , оно падает под юрисдикцией сборщика мусора и, следовательно, его уничтожение становится недетерминированным. Мне кажется, что этот аргумент недостаточно силен, преимущества RAII достаточно велики, чтобы сказать, что тип-значение с деструктором не может быть упакован (или использован в качестве члена класса).

Короче говоря, мой вопрос : есть ли другие причины, по которым типы значений нельзя использовать для представления RAII в .NET? (или вы думаете, что мой аргумент об очевидных преимуществах RAII неверен?)

Изменить. Я, должно быть, не четко сформулировал вопрос, так как первые четыре ответа упустили смысл. Я знаю о Finalize и его недетерминированных характеристиках, я знаю о конструкции using и чувствую, что эти два параметра хуже RAII. using - это еще одна вещь, которую должен запомнить потребитель класса (сколько людей забыли поместить StreamReader в блок using ?). У меня философский вопрос о языковом дизайне, почему он таков и может быть улучшен?

Например, с помощью общего детерминированного разрушаемого типа значения я могу сделать избыточными ключевые слова , использующие и lock (достижимые библиотечными классами):

    public struct Disposer<T> where T : IDisposable
    {
        T val;
        public Disposer(T t) { val = t; }
        public T Value { get { return val; } }
        ~Disposer()  // Currently illegal 
        {
            if (val != default(T))
                val.Dispose();
        }
    }
<Ч>

Я не могу не закончить приведенной цитатой, которую я однажды видел, но в настоящее время не могу найти ее происхождение.

  

Вы можете взять моё детерминированное уничтожение, когда моя холодная мертвая рука выходит из поля зрения. - Анон

Это было полезно?

Решение

Лучшим заголовком будет "Почему в C # / VB нет RAII". C ++ / CLI (эволюция абортов, которая была управляемой C ++) имеет RAII в том же смысле, что и C ++. Это всего лишь синтаксический сахар для того же шаблона финализации, который используют остальные языки CLI (деструкторы в управляемых объектах для C ++ / CLI фактически являются финализаторами), но он есть.

Вам может понравиться http://blogs.msdn.com /hsutter/archive/2004/07/31/203137.aspx

Другие советы

Отличный вопрос и тот, который очень беспокоил меня. Похоже, что преимущества RAII воспринимаются совсем по-другому. По моему опыту работы с .NET, отсутствие детерминированного (или, по крайней мере, надежного) сбора ресурсов является одним из основных недостатков. Фактически, .NET несколько раз вынуждает меня использовать целые архитектуры для работы с неуправляемыми ресурсами, которые могут (но могут и не требовать) требовать явного сбора. Что, конечно, является огромным недостатком, поскольку усложняет общую архитектуру и отвлекает внимание клиента от более важных аспектов.

У Брайана Гарри есть хороший пост об обоснованиях здесь .

Вот выдержка:

  

Как насчет детерминированной финализации и типов значений (структур)?

     

-------------- Я видел много вопросов о структурах, имеющих   деструкторы и т. д. Это стоит   комментарий. Есть множество   вопросы, почему некоторые языки не   есть они.

     

(1) композиция - они не дают вам   детерминированное время жизни в общем   кейс для однотипных композиций   причины, описанные выше. любой   недетерминированный класс, содержащий один   не будет вызывать деструктор, пока   в любом случае был завершен GC.

     

(2) копировать конструкторы - одно место   где это было бы действительно хорошо в   Стек выделяется местным жителям. Они будут   ограничен в методе, и все будет   отличный. К сожалению, чтобы получить   это действительно работает, вы также должны   добавить конструкторы копирования и вызвать их   каждый раз, когда экземпляр копируется.   Это один из самых уродливых и самых   сложные вещи о C ++. Вы в конечном итоге   получать код, выполняющийся во всем   место, где вы этого не ожидаете. Это   вызывает кучу языковых проблем.   Некоторые дизайнеры языка выбрали   держись подальше от этого.

     

Допустим, мы создали структуры с   деструкторы, но добавил кучу   ограничения, чтобы сделать их поведение   разумно перед лицом проблем   выше. Ограничения будут   что-то вроде:

     

(1) Вы можете объявить их только как локальные   переменные.      

(2) Вы можете только передать их   по-исх      

(3) Вы не можете назначить их, вы   Доступ только к полям и вызову   методы на них.      

(4) Вы не можете боксировать   их.      

(5) Проблемы с их использованием   Отражение (позднее связывание), потому что   обычно включает в себя бокс.      

может и больше,   но это хорошее начало.

     

Какая польза от этих вещей? Было бы   вы на самом деле создаете файл или   класс подключения к базе данных, который может   ТОЛЬКО использовать в качестве локальной переменной? я   не верь, что кто-то на самом деле   Что бы вы сделали вместо этого, это создать   соединение общего назначения, а затем   создать автоматически разрушенную оболочку для   использовать в качестве локальной переменной области видимости.   звонивший тогда выберет то, что они   хотел использовать. Обратите внимание, что абонент сделал   решение, и это не совсем   инкапсулируется в сам объект.   Учитывая, что вы могли бы использовать что-то   как предложения, поступающие в   пара разделов.

Замена RAII в .NET - это использование-шаблон, который работает почти так же хорошо, как только вы привыкнете к нему.

Самое близкое к этому - очень ограниченный оператор stackalloc.

Есть некоторые похожие потоки, если вы ищете их, но в основном это сводится к тому, что если вы хотите использовать RAII в .NET, просто реализуйте тип IDisposable и используйте " using " заявление, чтобы получить детерминистическое удаление. Таким образом, многие из тех же идеалов могут быть реализованы и использованы лишь немного более многословно.

ИМХО, большие вещи, которые нужны VB.net и C #:

  1. a " используя " объявление для полей, которое заставит компилятор сгенерировать код для удаления всех полей, помеченных таким образом. Поведение по умолчанию должно состоять в том, чтобы компилятор заставлял класс реализовывать IDisposable, если он этого не делает, или вставлял логику удаления перед запуском основной процедуры удаления для любого из ряда распространенных шаблонов реализации IDisposal, или же использовал атрибут, чтобы указать, что Утилизация должна проходить в рутине с определенным именем.
  2. средство детерминированного удаления объектов, чьи конструкторы и / или инициализаторы полей выдают исключение, либо с помощью поведения по умолчанию (вызов метода удаления по умолчанию), либо с помощью пользовательского поведения (вызов метода с конкретным именем) ,
  3. Для vb.net - автоматически сгенерированный метод для обнуления всех полей WithEvent.

    Все это можно довольно хорошо использовать в vb.net и несколько хуже в C #, но их первоклассная поддержка улучшит оба языка.

Вы можете создать форму RAII в .net и java, используя методы finalize (). Перегрузка finalize () вызывается перед очисткой класса GC, поэтому ее можно использовать для очистки любых ресурсов, которые абсолютно не должны храниться в классе (мьютексы, сокеты, дескрипторы файлов и т. Д.). Это все еще не детерминировано все же.

С помощью .NET вы можете делать это детерминистически с помощью интерфейса IDisposable и ключевого слова using, но у этого есть ограничения (использование конструкции, когда требуется для детерминированного поведения, по-прежнему нет освобождения детерминированной памяти, не используется автоматически в классах и т. д. ).

И да, я чувствую, что есть место для внедрения идей RAII в .NET и другие управляемые языки, хотя точный механизм может обсуждаться бесконечно. Единственная другая альтернатива, которую я мог видеть, - это ввести GC, который мог бы обрабатывать произвольную очистку ресурсов (не только памяти), но тогда у вас есть проблемы, когда указанные ресурсы абсолютно должны быть освобождены детерминистически.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top