Подтвердить что ты не робот

Принудительная сбор мусора или отражение в частном поле, которое менее злобно?

У нас есть сторонняя библиотека, которая внутренне использует SafeHandle для неуправляемого ресурса. В некоторых случаях ошибки необходимо избавиться от объекта и воссоздать его. Но есть ошибка в реализации dispose, которая препятствует закрытию Handle в подмножестве этих случаев. Это предотвращает успешное создание новых объектов до запуска финализатора.

Было предложено два решения (оба зла), чтобы мы могли устранить сторонний код:

  • Запустите GC.Collect, чтобы запустить финализатор и очистить объект

  • Используйте отражение, чтобы попасть в Handle и закрыть его, если dispose не удалось сделать это

какая из них менее злая и почему? Есть ли какой-то другой метод, который мы не считали менее злым, чем любой из этих?

4b9b3361

Ответ 1

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

...
thirdPartyObject.Dispose();
ThirdPartyDisposeBugWorkaround(thirdPartyObject);
...

void ThirdPartyDisposeBugWorkaround(ThirdPartyClass thirdPartyObject)
{
   //Do private reflection here
}

Принуждение GC, с другой стороны, имеет глобальный эффект. И есть много причин (большинство из них плохо) за вмешательство в GC. Это гораздо менее очевидно, что делает ваш код. Таким образом, вызов может быть сохранен даже после исправления ошибки.

Old New Thing: не использовать глобальное состояние для управления локальной проблемой

Ответ 2

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

try
{
   .. hacky reflection ..
}
catch(Exception ex)
{
    throw new Exception("Reflection on private field 'Xyz' of 3rd Party Component 'Abc' failed.  Was 'Abc' updated? Reflection is used due to bug in 'Dispose' implementation.", ex);
}

Ответ 3

Сначала выберите тот, который работает. Если они оба работают, выберите тот, который имеет наименьшее влияние на систему. Gc.Collect - это скорее молоток по всему приложению. Где, когда ваш код отражения является хрупким, но должен иметь очень небольшое влияние.

Ответ 4

Если он не помечен как запечатанный, вы можете наследовать его и реализовать свой собственный распоряжаться. Что касается отражения против GC, я бы определенно использовал отражение. Как говорили другие, GC может работать не так, как ожидалось. Он мог бы выполнять итерацию коллекции, но фактически не выпускал ваш дескриптор.

Я хотел бы отметить: если что-то еще имеет ссылку на этот SafeHandle, и вы его отпустите, вы можете легко ввести другие ошибки в свою систему.

Ответ 5

Принимая аргумент "CodeInChaos", почему вы не вызываете сбор для определенного поколения.

GC.GetGeneration(Object obj) вернет поколение, в котором находится объект, и GC.Collect(Int32 gen).

Пример:

Int32 generation = GC.GetGeneration(theObject);
theObject = null;
GC.Collect(generation);
GC.WaitForPendingFinalizers();
GC.Collect(generation); // this is req because first collect puts thisObject on freachable queue not // garbaged yet.