Требуется ли GC.KeepAlive здесь, или я могу полагаться на локальных жителей и аргументы, сохраняя объект живым?

У меня есть куча методов, которые принимают WPF WriteableBitmap и читают с его BackBuffer напрямую, используя небезопасный код.

Не совсем ясно, следует ли использовать GC.KeepAlive всякий раз, когда я делаю что-то вроде этого:

int MyMethod(WriteableBitmap bmp)
{
    return DoUnsafeWork(bmp.BackBuffer);
}

С одной стороны, остается ссылка на bmp на стек MyMethod. С другой стороны, похоже, что он полагается на детали реализации - это может скомпилировать хвостовой вызов, например, не содержать ссылки на bmp, когда вводится момент DoUnsafeWork.

Аналогично, представьте себе следующий гипотетический код:

int MyMethod()
{
    WriteableBitmap bmp1 = getABitmap();
    var ptr = bmp.BackBuffer;
    WriteableBitmap bmp2 = getABitmap();
    return DoUnsafeWork(ptr, bmp2);
}

В теории ссылка на bmp1 остается в стеке до тех пор, пока метод не вернется, но опять же, похоже, используется деталь реализации. Разумеется, компилятор может объединить bmp1 и bmp2, потому что они никогда не живут одновременно, и даже если компилятор никогда не делает этого, JITter все еще может и, возможно, делает это (например, сохраняя их как в тот же регистр, первый, затем другой).

Итак, в общем: следует ли полагаться на локальные/аргументы, являющиеся действительными ссылками на объект, или я всегда должен использовать GC.KeepAlive для гарантии правильности?

Это особенно озадачивает, так как, по-видимому, FxCop считает, что GC.KeepAlive всегда плох.

Ответ 1

Должен ли я полагаться на locals/arguments, являющиеся допустимыми ссылками на объект?

Нет. Ваш анализ правильный; джиттер полностью находится в пределах своих прав, чтобы сообщить сборщику мусора, что содержимое локальных мертвых в тот момент, когда они больше не используются управляемым кодом.

Должен ли я всегда использовать GC.KeepAlive, чтобы гарантировать правильность?

Да. Это для чего.

Ответ 2

Если вы вызывали WriteableBitmap.Lock перед тем, как получить задний буфер, тогда WriteableBitmap должен быть закреплен, а основная память не будет перемещена. По крайней мере, моя интерпретация WriteableBitmapAPI.

Я активно использовал WriteableBitmap, в частности, библиотеку WriteableBitmapEx с открытым исходным кодом на Codeplex (раскрытие, я внес один раз в эту библиотеку, но это не мой проект). Это использует шаблон Lock/Access Backbuffer (повторно)/unlock/Invalidate для рисования. У меня никогда не было проблемы с этим шаблоном, хотя backbuffer IntPtr хранится в структуре и передается вокруг приложения.

С уважением,