2016-04-20 2 views
2

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

Один из моих разработчиков написал этот код:

// 

    // ValidationDataTable is a typed DataTable, generated by the Framework 
    ValidationDataTable validationTable; 
    using (ValidationTableAdapter adapter = new ValidationTableAdapter()) { 
     using (validationTable = adapter.GetData()) { } 
    } 

    datafeedValidators.Add(new CountryFieldValidator(validationTable.ToDictionary(key => key.CountryCode, value => value.CountryName))); 

    // Party on... 

// 

Моего понимание: validationTable были захоронены, но не мусор, когда он упоминается в последней строке коды - но все равно должен быть бросил ObjectDisposedException на .ToDictionary() вызов. Но этот код с радостью создает действующий словарь и продолжает работать.

У меня есть теории, но не могу найти ничего определенного, чтобы подтвердить или сбить любого из них. И код можно переписать дюжину способов избежать проблемы; это не проблема. Мне просто нужно знать, в чем разница в моем понимании.

Мои вопросы:

  1. Является ли этот код действует и ведет себя так, как надо?
  2. Если нет, то успех, который мы видим, просто дерьмо?
  3. Есть ли что-то конкретное о DataTable, что позволяет получить доступ после объекта расположен - аналогично тому, как GZipStream класса требует, чтобы избавиться от объекта для очистки потока, и, следовательно, позволяет делать звонки .ToArray() и .GetBuffer() после того, как объект имеет были утилизированы?
  4. ... Что на самом деле вызывает исключение ObjectDisposedException при вызове методов? Я всегда предполагал, что это происходит из самой платформы .NET.

.

ПОЯСНЕНИЯ:

Это .NET Framework вопрос. Консенсус в том, что мое понимание верное - самому DataTable придется выбросить ObjectDisposedException. Кроме того, что это не так. Не где-нибудь в исходном коде DataTable, поэтому я спрашиваю. Я предположил, что структура обеспечит исключение ObjectDisposedException после его размещения, что, по-видимому, не так ... в отличие от GZipStream, который разрешает доступ только к двум методам после Dispose(), DGAF DataTable. Хорошо.

Так что позвольте мне перефразировать вопрос: есть ли что-то внутреннее в DataTable, которое будет бомбить нас, потому что разрешены вызовы на расположенную таблицу? Я могу предположить, что Microsoft не очистила что-либо внутренне, что все свойства и значения будут там, нетронутыми, до тех пор, пока объект находится в области видимости - это просто не похоже на безопасное предположение. Этот код уходит независимо - я просто хотел понять, была ли умышленная причина, по которой Microsoft разрешает доступ к DataTable после Dispose(), или это был надзор, а не забота и т. Д.

Также, если вы опротете вопрос или сделаете голосование чтобы закрыть его, пожалуйста, оставьте комментарий почему.

+4

5. Если этот код заставляет членов команды делать двойной прием и нелегко понять (настроение, с которым я согласен), тогда его следует изменить в любом случае, если только по какой-либо причине, кроме поддержки команды. И этот разработчик должен приложить больше усилий для поддержки и меньше усилий, чтобы быть умным. Полностью пустые блоки кода должны быть красным флагом для любого, кто пишет код. – David

+0

Да, это так. К сожалению, я не уверен, что он настолько умный, что это недостаток понимания со стороны разработчиков. Также может быть усталой ошибкой: P –

+2

Согласен с @David. Пустой блок обычно означает, что вы делаете это неправильно. Краткие ответы: 1. возможно, 2. возможно, нет, 3. да - 'DataTable' на самом деле ничего не делает *, когда он расположен. Это источник некоторого разногласия, но, чтобы быть ясным, утилизация DataTable ничего не делает. 4. Это зависит от реализации конкретного класса. Ни структура, ни язык не заботятся об утилизации. Я бы посоветовал вам ознакомиться с одноразовым рисунком. –

ответ

5

Я думаю, что часть, которую вам не хватает, заключается в том, что «удаление» объекта не делает ничего, кроме того, что программист определил в реализации IDisposable.Dispose. Язык или структура не делает ничего особенного, кроме поддержки поддержки инструкции using.

С using заявлением, язык только обеспечивает следующее: если объект реализует этот конкретный интерфейс, который называется IDisposable, то он обещает вызвать метод Dispose, когда он существует using блока. Вот и все. Он не знает, какие объекты были «удалены» или нет. Он не бросает ObjectDisposedException путем отслеживания расположенных объектов особым образом.

Что бросает ObjectDisposedException?Ну, программист, который реализуется, что IDisposable тип должен был бы написать код, как это где-то там:

void DoMoreWork() 
{ 
    if(_iHaveBeenDisposedAlready) 
     throw new ObjectDisposedException(null); 
    ... 

Так что в вашем случае, если ValidationDataTable был реализован таким образом, что не следит ли ему был установлен или нет, и он хранит данные в памяти, тогда он будет работать как обычно. Язык или рамки не останавливают это.


UPDATE: отвечать на комментарии, похоже DataTable не напрямую реализовать IDisposable, но это базовый класс (MarshalByValueComponent) делает. Они должны были наследовать от этого базового класса, чтобы поддержать опыт дизайнера WinForms. Вне режима разработки Dispose ничего не мутирует. Таким образом, вы можете спокойно игнорировать его для вашего обычного использования. Другими словами, вам не нужно использовать его в блоке using.

Это нормально? Нет. Обычно объекты IDisposable предназначены для размещения где-то по их нормальному жизненному циклу. Разумеется, смущает наличие IDisposable, который не требует утилизации.

+0

Я уточнил код, чтобы сказать, что 'ValidationDataTable' является типизированным DataTable, созданным платформой .NET. Я думаю, что плохо сформулировал проблему - я пытаюсь понять, почему структура разрешит доступ к элементам DataTable после Dispose. Не копаясь через исходный код рамки в иерархии наследования DataTable, это похоже на игру в гольф на минном поле. –

+0

Кроме того, мы не сохраняем этот код ... Я просто хотел понять, что происходило за кулисами с помощью DataTable. Это случай, как GZipStream, где доступ был разрешен намеренно? Надзор? Или MS просто не заботится. –

+0

@JamesKing Посмотрел исходный код DataTable и обновил свой ответ. Это преднамеренно, но если вы спросите MS об этом, я очень сомневаюсь, что они подумают, что это идеально. –

-4

Согласно documentation on using:

Как правило, при использовании IDisposable объекта, вы должны объявить и создать его экземпляр в использовании заявления. Оператор using вызывает метод Dispose на объекте правильным образом и (когда вы используете его, как показано выше), он также приводит к тому, что сам объект выходит из области действия, как только вызывается Dispose.Внутри используемого блока объект доступен только для чтения и не может быть изменен или переназначен.

  1. Да. См. Цитату ниже.
  2. N/A
  3. Н/Д. Если вы посмотрите на IDisposable.Dispose(), он заявляет: «Выполняет задачи, определенные приложением, связанные с освобождением, выпуском или сбросом неуправляемых ресурсов». Если функциональность может быть предоставлена ​​без управляемых ресурсов, то для предотвращения доступа к этой функции не требуется удаление объекта.
  4. Разработчик класса, который реализовал метод или свойство, к которому вы обращаетесь, добавляет код, чтобы определить, находится ли объект и выбрасывает исключение по мере необходимости.

Также из documentation on using:

Вы можете создать экземпляр объекта ресурса, а затем передать переменную в использовании заявление, но это не лучшая практика. В этом случае объект остается в области действия после того, как элемент управления покидает блок использования, даже если он, вероятно, больше не будет иметь доступа к своим неуправляемым ресурсам. Другими словами, он больше не будет полностью инициализирован. Если вы попытаетесь использовать объект вне блока использования, вы рискуете вызвать исключение. По этой причине, как правило, лучше создавать экземпляр объекта в инструкции using и ограничивать его область применения блоком использования.

Короче говоря, validationTable не располагают и больше не имеет доступ к своим неуправляемым ресурсам, но управляемые ресурсы (локальная копия данных) по-прежнему доступны. Это предполагает, что ValidationDataTable был реализован правильно. Поскольку я не нахожу его, хотя Google или msdn, я предполагаю, что это класс в доме, так что все идет.

+1

_ "validationTable не объявляется и не создается в операторе using, поэтому он не будет удален" _ - Это неверно. Оператор 'using' _allows_ переменная, которая будет объявлена ​​в инструкции, но не запрашивает ее. Объект, на который ссылается, все равно будет удален. –

+0

@PeterDuniho Это было исправлено. Также, пожалуйста, предоставьте доказательства, подтверждающие ваши заявления.Если я скажу «небо фиолетовое», и вы говорите «нет, небо зеленое», ничего не меняется. – Trisped

0

Как отмечает Ли в своем комментарии, DataTable является одноразовым, поскольку он наследует это от MarshalByValueComponent. Это случайно, что Dispose() не делает ничего, что могло бы вызвать выброшенное исключение. Ну, на самом деле это не авария как таковая, но нет ничего, что помешало бы более поздней версии фреймворка сделать что-то, что сделал.

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

Смежные вопросы