Поля Readonly становятся нулевыми при удалении из финализатора
У меня есть следующий класс. Теперь иногда оператор блокировки бросает ArgumentNullException
, и в этом случае я могу увидеть в отладчике, что объект disposelock
действительно имеет значение null.
Как я вижу, что утилизация ложна, я знаю, что метод запускается из Finalizer.
Но как это может произойти? Он определяется как readonly и получает его значение при создании объекта.
PS: Я знаю, что это не очень хороший шаблон, но его часть данного кода, и я просто не могу объяснить, почему это становится null
public abstract class DisposableMarshalByRefObject : MarshalByRefObject, IDisposable
{
private readonly object disposeLock = new object();
/// </summary>
~DisposableMarshalByRefObject()
{
Dispose(false);
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing) //disposing = false,=> finalizer
{
lock (disposeLock) //ArgumentNull Exception !
{
....
}
}
}
Ответы
Ответ 1
В сборке мусора порядок этой коллекции не определен:
- Первый
this
собран
- Далее
disposeLock
собирается
или
- Первый
disposeLock
собирается
- Далее
this
собирается
Поэтому не используйте никаких ссылочных полей (такие структуры, как int
, bool
и т.д.) в Dispose(false);
protected virtual void Dispose(bool disposing) {
if (disposing) {
// Explicit disposing: it safe to use disposeLock
lock (disposeLock) {
...
}
}
else {
// Garbage collection: there no guarantee that disposeLock has not been collected
}
}
Ответ 2
Все существующие ответы, кроме ответа на отражение, являются ложными. GC не устанавливает ссылки на null при сборе объектов. Доступ к объекту не вызвал ложного отказа из-за GC. Порядок финализации undefined, но все существующие ссылки на объекты продолжают существовать и действительны.
Мое предположение о том, что произошло: конструктор был прерван до того, как поле было инициализировано. Это оставило поле null
. Затем финализатор нашел это так.
Конструктор может быть прерван путем выброса исключения или вызовом Thread.Abort
, которое является злым.
В сборке мусора порядок этой коллекции не определен
Порядок сбора не является наблюдаемым (за исключением слабых ссылок...). Порядок завершения является наблюдаемым, но не с оператором lock
, потому что объекты не теряют возможности синхронизации при завершении.
Ответ 3
Поскольку вы подчеркнули readonly
, немного разъясните об этом. Время выполнения не предотвращает изменения полей readonly
. Независимо от readonly
от С# становится initonly
в IL.
Например, вы можете легко изменить поле readonly
с помощью отражения:
class A
{
private readonly int bar;
public A()
{
bar = 1;
}
public void Foo()
{
Console.WriteLine(bar);
}
}
var a = new A();
a.Foo(); // displays "1"
a.GetType().GetField("bar", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(a, 2);
a.Foo(); // displays "2"
Конечно, это не означает, что вы должны проверять эти поля для null
каждый раз, но могут быть случаи, когда поле readonly
изменяется (вы столкнулись с одним из них).
Как примечание. Вам действительно нужен финализатор? Я имею в виду, есть ли какие-то истинные неуправляемые ресурсы?