Strange Queue <T>.Enqueue(T item) код
Во время отражения с помощью ILSpy я нашел эту строку кода в Queue<T>.Enqueue(T item)
-методе:
if (this._size == this._array.Length)
{
int num = (int)((long)this._array.Length * 200L / 100L);
if (num < this._array.Length + 4)
{
num = this._array.Length + 4;
}
this.SetCapacity(num);
}
Мне просто интересно, почему кто-то это сделает? Я думаю, что это какая-то целая проверка переполнения, но зачем сначала умножать на 200L
, а затем делить на 100L
?
Возможно, это была проблема с более ранними компиляторами?
Ответы
Ответ 1
Обычно вещи, которые сначала умножаются, а затем делятся на 100, представляют собой процентные вычисления. Возможно, в исходном коде было несколько const XxxPercentage = 200
или что-то в этом роде. Компилятор, похоже, не оптимизирует * 200 / 100
до * 2
.
Этот код устанавливает емкость в два раза больше его размера, но если в два раза его размер будет меньше исходного размера + 4, используйте это вместо.
Причина, по которой он конвертируется в long, вероятно, состоит в том, что если вы умножаете целое число на 200 процентов, оно будет переполняться.
Ответ 2
Ниже приведены все то же самое и будет генерировать тот же результат:
int size = (int)((length * 200L) / 100L);
int size = length << 1;
int size = length * 2;
Причина выбора первого варианта над другим заключается в том, чтобы четко показать ваше намерение:
const long TotalArraySize = 200L;
const long BytesPerElement = 100L;
return (length * TotalArraySize) / BytesPerElement;
Ниже приведены некоторые сведения о последствиях производительности: Удвоение числа - сдвиг влево и умножение
Ответ 3
Если вы продолжаете искать реализацию Queue, вы найдете следующие поля:
const int _GrowFactor = 200;
const int _MinimumGrow = 4;
Интересно, что эти константы не используются:) Я думаю, что эти константы были жестко запрограммированы (фактор роста также заменен длинным типом). Давайте посмотрим на метод Enqueue с этой точки зрения:
if (this._size == this._array.Length)
{
int capacity = (int)((this._array.Length * _GrowFactor) / 100L);
if (capacity < (this._array.Length + _MinimumGrow))
{
capacity = this._array.Length + _MinimumGrow;
}
this.SetCapacity(capacity);
}
Я думаю, что эти имена имеют смысл. GrowFactor указывает в процентах, сколько массива должно расти. По умолчанию это 200%. Но они также указали минимальный рост для внутреннего массива. Таким образом, если массив не увеличился настолько, насколько текущая длина + минимум растет, мы дадим это минимальное увеличение в любом случае.
Ответ 4
Намерение * 200L / 100L
не яснее, чем * 2
, на мой взгляд. Единственная причина, по которой я могу думать о том, почему это делается, заключается в том, чтобы убедиться, что длина очереди может вырасти до 200 раз. Существует разница в * 200 / 100
и * 2
, так что первая приведет к исключению переполнения для 100-кратного меньшего числа. Например, если это было для значений байта, x * 200 / 100
завершится с ошибкой для x == 2, но * 2
завершится с ошибкой, только если x будет размером 128.
Но как заметил Марсело Кантос, (long)this._array.Length * 200L / 100L
никогда не переполнится, поэтому мой ответ, вероятно, мало помогает.
Может ли это быть "функцией" ILSpy? Возможно, в исходном коде это просто * 2
.
ИЗМЕНИТЬ
После дополнительного исследования выясняется, что этот странный код должен быть артефактом некоторого рефакторинга. Я проверил, как это делается в List < >
private void EnsureCapacity(int min)
{
if (this._items.Length < min)
{
int num = (this._items.Length == 0) ? 4 : (this._items.Length * 2);
if (num < min)
{
num = min;
}
this.Capacity = num;
}
}
Это так же просто, как и следовало ожидать. Я предполагаю, что код Queue.Enqueue
был переработан, но не полностью очищен, и из этого изменения появился какой-то странный код. Большинство разработчиков считают, что библиотеки Microsoft идеальны, и все имеет смысл, но вполне вероятно, что не каждая строка кода написана гением: -)