Почему в .NET нет RAII? - PullRequest
       35

Почему в .NET нет RAII?

64 голосов
/ 06 октября 2008

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

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

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

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

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

Например, с помощью общего детерминированного разрушаемого типа значения я могу сделать избыточными ключевые слова 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();
        }
    }

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

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

Ответы [ 7 ]

16 голосов
/ 06 октября 2008

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

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

14 голосов
/ 06 октября 2008

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

14 голосов
/ 06 октября 2008

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

1 голос
/ 14 декабря 2010

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

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

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

1 голос
/ 06 октября 2008

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

1 голос
/ 06 октября 2008

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

0 голосов
/ 06 октября 2008

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

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

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

...