2015-05-27 1 views
3

Рассмотрим следующий код:C# вызова IDisposable.Dispose() против создания объекта Null

А. Игрушка класса

class Toy 
{ 
    private string name; 
    public string Name 
    { 
     get { return name; } 
     set { name = value; } 
    } 

    private ToyAddon addOn; 
    public ToyAddon AddOn 
    { 
     get { return addOn; } 
     set { addOn = value; } 
    } 

    public void RemoveAddons() 
    { 
     /* 
     * someone said: this could lead to possible memory leak 
     * if this class implements IDisposable, 
     * then we are not disposing any of the resource 
     * */ 
     //this.addOn = null; 

     /* instead use this */ 
     var disposableToyAddOn = this.addOn as IDisposable; 
     if (disposableToyAddOn != null) 
      disposableToyAddOn.Dispose(); 

     this.addOn = null; 

    } 
    public override string ToString() 
    { 
     return String.Format("{0}, {1}", 
      this.name, 
      (this.AddOn == null) ? "" : addOn.AddOnName); 
    } 
} 

B. Игрушка Аддон

class ToyAddon 
{ 
    private string addOnName; 

    public string AddOnName 
    { 
     get { return addOnName; } 
     set { addOnName = value; } 
    } 

} 

C. Основная программа

class Program 
{ 
    static void Main(string[] args) 
    { 
     ToyAddon tAdd = new ToyAddon(); 
     tAdd.AddOnName = "Amazing AddOn"; 
     Toy t = new Toy(); 

     t.Name = "Amazing Toy"; 
     t.AddOn = tAdd; 
     t.RemoveAddons(); 

     Console.WriteLine(t.ToString()); 


    } 
} 

Теперь было предложено d, чтобы проверить, реализует ли объект «having-a» IDisposable, затем вызвать метод dispose. (пожалуйста, ознакомьтесь с комментариями в Toy class)

IMHO, если я сделаю ссылку нулевой, то объект в куче будет помечен для Collection by GC.

Было бы понятно, если бы эти рассуждения могут быть сделаны на то, что происходит на куче и стека, а также роль GC в том, что если класс (как ToyAddOn) реализует IDisposable против, если он не делает.

+4

Я рекомендую действительно хорошее сообщение в блоге Стивена Клири "[IDisposable: Что ваша мать никогда не говорила вам о освобождении ресурсов) (http://www.codeproject.com/Articles/29534/IDisposable-What-Your-Mother -Никогда-Told-You-О)». Вероятно, вам понадобится около часа, чтобы прочитать, но вы узнаете много, и у вас будет намного лучшее понимание, почему ваш коллега рассказал вам, что он сказал вам. –

+0

Это не полный, и не такой простой. Вам нужен «контракт», в котором говорится, что Toy «владеет» AddOn и что AddOns нельзя использовать повторно. Один из способов выразить это - переименовать RemoveAddons -> DestroyAddons –

+0

downvote без причины !! Strange –

ответ

2

Теперь он предложил мне проверить, если «иметь-а» объект реализации IDisposable затем вызвать метод избавиться от него.

IDisposable - это шаблон, который используется для освобождения неуправляемых ресурсов, которые должны быть явно выпущены, поскольку GC не знает о них. Обычно класс, в котором хранятся неуправляемые ресурсы, также реализует финализатор, и для этого он фактически продлевает срок службы объекта, более чем необходимо. Dispose в таких ситуациях обычно вызывает вызов GC.SupressFinalize, чтобы удалить указанный объект из очереди финализации.

Как правило, хороший шаблон, если у вас есть объект IDisposable, для реализации IDisposable самостоятельно, чтобы убедиться, что базовый ресурс удален.

IMHO, если я сделаю ссылку нулевой, тогда объект на куче будет , обозначенный как Collection by GC.

Если участник, которого вы хотите «аннулировать», не является членом static, то есть (как правило) нет причин для этого. Компилятор достаточно умен, чтобы оптимизировать его, и GC достаточно умен, чтобы знать, что больше нет ссылки на переменную и очистите ее.

Редактировать:

В @ScottChamberlain указывает, есть случаи, когда одноразовый предмет все еще удерживается ссылку, и, следовательно, ГХ не рассчитывает его для GC. Когда вы удаляете его и обнулите его, вы намекаете GC, что он «готов к сбору».

+0

Одной из причин отказа является уменьшение размера графа объекта. Кто-то может избавиться от вашего класса, но не освободить ссылку, исключив доступные ресурсы ваших классов, когда вы будете настроены, вы позволяете своим детям собираться, даже если вы еще не можете. –

+0

@ScottChamberlain Я понимаю, что вы имеете в виду. Хотя это звучит как конкретный случай края. Однако я добавлю это к ответу. Благодарю. –

+0

@downvoter - Объясните, что вы нашли не так с этим ответом. –

1

Если вы не Dispose класса вручную неуправляемые ресурсы (если таковые имеются), то справедливо будет очищен только когда ГХ обнаруживает там достаточное давление памяти требует сбора мусора и объект получает GCed и Завершается.

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

Если давление памяти никогда не становится достаточно высоким, возможно, что вы никогда не мусор собираете эти значения, прежде чем вы исчерпаете ресурс, который выдает. Вы часто это видите, когда люди кладут Bitmap в узком цикле, но не уничтожают их, каждый битмап использует дескриптор GDI + и не учитывает давление управляемой памяти и не запускает GC. Это часто заставляет людей получать OutOfMemoryExceptions, когда у них все еще много памяти, потому что реальная вещь, из которой они закончились, - это дескрипторы GDI +.

Путем явного распоряжения своими ресурсами вам не нужно «надеяться, что вам повезет», и ваш объект будет завершен до того, как вы исчерпаете неуправляемые ресурсы, которые он удерживает.

Стек и куча не имеют ничего общего с этим.

+0

GC не знает интерфейса 'IDisposable' и поэтому никогда не вызывает' .Dispose() '. Вы должны явно называть это. – Enigmativity

+0

Я не думаю, что это достаточно ясно. Когда вы говорите: «Явным образом избавляясь от ваших ресурсов, вам не нужно« надеяться, что вам повезет », и ваш объект получит GCed.« Похоже, вы говорите, что GC вызывает «.Dispose()». – Enigmativity

+0

@ Энигматичность. Я изменил ее, чтобы сказать «Finalized» вместо «GCed» и несколько других небольших изменений в формулировке, надеюсь, что это очистит, что я говорю не о Dispose(), а вместо этого говорю о процессах финализации. –

1

Образец пробного литья что-то IDisposable и утилизация его, если он реализует этот интерфейс, очень показателен к недостатку конструкции. Если тип эталонного кода трюмов не реализует IDisposable, что это очень сильный признак того, что либо:

  1. Держатели ссылки этого типа не должен быть располагающим его; даже если производные типы реализуют Disposable, ответственность за их очистку должна лежать на коде, который создал экземпляры этих типов (и содержит ссылки этих типов).

  2. Базовый тип должен быть реализован IDisposable (даже если 99% реализации не будет делать ничего), потому что некоторые экземпляры будут иметь время жизни вне контроля тех, кто знает, действительно ли они на самом деле нужна ыборка. Ярким примером этих последних ситуаций является IEnumerator<T>. Хотя IEnumerator опущен IDisposable, потребность в нем стала очевидной вскоре после выпуска; когда Microsoft добавила IEnumerator<T>, они включили IDisposable напрямую. Хотя большинство реализаций IEnumerator<T> можно оставить без очистки, нет никакого способа, чтобы код, вызывающий GetEnumerator в коллекции, не имел возможности узнать, может ли реализация IEnumerator<T> быть одним из редких, нуждающихся в очистке, и нет практического способа IEnumerable<T>, способ которого GetEnumerator создает IEnumerator<T>, можно ожидать, что когда клиент будет с ним выполнен, никто, кроме клиента, не сможет DisposeIEnumerator<T>.

Определить, является ли какие-либо случаи ToyAddOn, реализующие Disposable есть у всех, вероятно, чтобы их пожизненный конца в то время, когда в то время как только ссылки проводятся с помощью кода, который не имеет ни малейшего представления, нужны ли они очистки. Если это так, сделайте ToyAddOn орудие Disposable. Если нет, оставьте очистить код, который знает, что это необходимо.

1

Я согласен с этими тремя ответами. Просто чтобы упростить ответ на ваш вопрос:

/* * кто-то сказал: это может привести к возможной памяти утечки *, если этот класс реализует IDisposable, *, то мы не утилизацией любого ресурса * */ // это.addOn = null;

То, что кто-то сказал, не подходит для вашего примера. Но это абсолютно правильно, в общем.

Предположим, что класс Toy живет намного дольше, чем ToyAddon. Класс ToyAddon имеет подписку на события в Toy Class. Если это так, ToyAddon должен отказаться от подписки в методе Dispose. Если вы не вызвали метод Dispose ToyAddon после удаления его из экземпляра Toy, ToyAddon instace будет жить в памяти до тех пор, пока экземпляр Toy будет жить.

И даже если вы звоните в Dispose в приведенном выше случае, все еще есть ссылка на ToyAddon через 'addOn'. Поэтому для того, чтобы полностью избавиться от него, необходимо установить addOn в null.

IMHO, если я сделаю ссылку нулевой, тогда объект на куче будет , обозначенный как Collection by GC.

Правильно в вашем случае, неправильно, как я объяснил выше.