У меня возникают трудности с шаблоном С# Dispose. У меня здесь 3 класса: класс управления, форма и класс хранения данных.
Класс управления может (если необходимо) использовать форму для запроса пользователя для ввода. Форма загружает данные из файла, которые пользователь может затем изменить. Когда он закрыт, форма должна сохранить эти данные. Класс хранения данных реализует .Dispose(), который делает именно это - записывает изменения на диск.
Поскольку этот класс хранения данных (StoredInfo) является членом формы (MyForm), MyForm также должен реализовать .Dispose(), чтобы вызвать StoredInfo.Dispose. Это то, что дает мне проблемы. Мой класс управления, в нем код:
Form.Dispose();
Form = null;
И моя форма:
// As written by MSVS. The exception is OK - I just want this to get called.
#region IDisposable Members
public void IDisposable.Dispose()
{
throw new NotImplementedException();
}
#endregion
... но метод Form.Dispose() никогда не вызывается. Выйдя с отладчиком, выполнение выполняется:
Connector.Dispose() // The management class
Form.Dispose()
Form.Dispose(bool disposing) // (1) Some method designer wrote?
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
Connector.Dispose() // Back to the management class!
Form = null;
Мы почему-то никогда не вызывали .Dispose, но вместо этого вызываем .Dispose(bool). В С++, где аргументы могут иметь значения по умолчанию, я могу это увидеть, но в С# я потерян. Мое лучшее предположение заключается в том, что мой отладчик не показывает мне, что на самом деле происходит.
Теперь, глядя на иерархию классов, я вижу другие классы, которые реализуют IDisposable, поэтому должен быть другой объект Dispose(). Это не виртуально, поэтому я не уверен, почему я не получаю ошибки компилятора. Я попытался переопределить метод .Dispose(bool), так как он получает вызов и является виртуальным, но с этим:
protected override void Dispose(bool disposing)
{
StoredHosts.Dispose();
}
Я получаю "Тип ConnectorForm" уже определяет член, называемый "Dispose" с теми же типами параметров ", который, да, я предполагаю, что он... в коде конструктора. Так что не вариант. Поэтому вернемся к вызову Dispose(). Но как? Мне не хватает простой простоты и мощности и детерминизма деструкторов С++ на данный момент.
Мастер Windows Form ставит специальные "региональные директивы" вокруг кода, который вы не должны изменять, поэтому вы можете изменять материал Dispose
столько, сколько хотите, до тех пор, пока вы остаетесь внутри шаблона.
Подумайте о IDisposable как способе создания деструкторов .NET. Пока все разработчики правильно это понимают, результат может быть эквивалентен деструкторам С++ (на самом деле С++/CLI генерирует метод Dispose из объявлений деструктора, и я очень скучаю по этой функции на С#).
Прочитайте это для некоторого фона: http://msdn.microsoft.com/en-us/magazine/cc163392.aspx
Есть несколько вещей, о которых нужно знать. Во-первых, параметр disposing
указывает на то, что в кат-контексте вызывается виртуальный метод Dispose(bool)
. Если это false
, то DO NOT DO ANYTHING!
Это означает, что вы вызываетесь из потока финализатора. Это почти никогда не бывает полезным, и это исторический недостаток в дизайне этого шаблона, потому что у него есть что-то 99,99% полезное (логика детерминированного уничтожения), смешанное с чем-то полезным только на 0,01% (обычная бесплатная потоковая обработка собственных ресурсов), как если бы они были лучшими друзьями.
Итак, разместите свой собственный код очистки внутри ветки if (disposing)
.
Во-вторых, обратите внимание на то, как код, созданный мастером, проверяет, не является ли ссылка объекта не нулевой, прежде чем называть Dispose
. Согласно определению IDisposable
, вы должны ожидать, что Dispose
будет вызван несколько раз без причины в одном экземпляре.
Итак, вы хотите сделать это:
if (Form != null)
{
Form.Dispose();
Form = null;
}
Класс Form
(или, если быть точнее, класс Component
) определяет свой собственный метод Dispose
, который вызывает виртуальный метод Dispose(bool disposing)
Вам нужно переместить созданный конструктором метод Dispose
(который переопределяет виртуальный метод и вызывается Component.Dispose
) из файла Designer, а затем помещает StoredHosts.Dispose();
внутри него.
Там нет правила, говорящего, что вы не можете возиться с тем, что сделал дизайнер, до тех пор, пока вы помните, чтобы не сломать его. Не стесняйтесь добавлять в конструктор Dispose()
метод или удалять его и записывать в основной исходный файл.
Дизайнер не волшебный. Он просто пишет нормальный С#.
написав void IDisposable.Dispose(), вы фактически сообщаете runtime вызывать эту конкретную версию метода Dispose тогда и только тогда, когда переменная имеет тип IDisposable.
например.
Form f1 = new YourForm();
IDispoable f2 = new YourForm();
f1.Dispose(); //call to public void Dispose()
f2.Dispose(); //call to void IDisposable.Dispose()