奇怪的队列<;T>;.排队(T项目)代码
本文关键字:项目 代码 排队 gt lt 队列 | 更新日期: 2023-09-27 18:28:59
在使用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
?
这可能是早期编译器的问题吗?
通常情况下,先乘以100再除以100是百分比计算——也许在原始代码中有一些const XxxPercentage = 200
或类似的东西。编译器似乎没有优化* 200 / 100
到* 2
。
这段代码将容量设置为其大小的两倍,但如果两倍的大小将小于原始大小+4,则使用该大小。
它被转换为long的原因可能是,如果将整数乘以"200%",它就会溢出。
以下都是相同的,将生成相同的结果:
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;
这里给出了一些关于性能影响的细节:左移数倍与乘法
如果您继续寻找队列实现,您会发现以下字段:
const int _GrowFactor = 200;
const int _MinimumGrow = 4;
有趣的是,这些常数没有被使用:)我认为这些常数是硬编码的(growtfactor也被long类型取代)。让我们从这个角度来寻找排队方法:
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%。但他们也为内部数组指定了最小增长。所以,若数组并没有像当前长度+最小增长一样增长,我们无论如何都会给出这个最小增长。
在我看来,* 200L / 100L
的意图并不比* 2
更清晰。我能想到为什么这样做的唯一原因是确保队列长度可以增长到200倍。* 200 / 100
和* 2
存在差异,第一个会导致小100倍的溢出异常。例如,如果是字节值,则x * 200 / 100
将在x==2时失败,但* 2
仅在x大到128时失败。
但正如Marcelo Cantos所指出的,(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库是完美的,一切都有意义,但很可能并不是每一行代码都是由天才编写的:-)