在与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
?
这可能是早期编译器的一个问题吗?
答案 0 :(得分:4)
以下都是相同的,并会产生相同的结果:
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;
此处提供了有关性能影响的一些详细信息:Doubling a number - shift left vs. multiplication
答案 1 :(得分:4)
通常首先乘以然后除以100是百分比计算 - 也许在原始代码中有一些const XxxPercentage = 200
或类似的东西。编译器似乎没有优化* 200 / 100
到* 2
。
此代码将容量设置为其大小的两倍 - 但如果其大小的两倍小于原始大小+ 4,则使用该代码。
它被转换为long的原因可能是因为如果你将整数乘以“200%”它会溢出。
答案 2 :(得分:4)
如果您继续寻找队列实施,您将找到以下字段:
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%。但他们还指定了内部阵列的最小增长。因此,如果数组没有像当前长度+最小增长那样增长,那么无论如何我们都会给这个最小的增长。
答案 3 :(得分:0)
在我看来,* 200L / 100L
的含义不再比* 2
更明确。我能想到为什么这样做的唯一原因是确保队列长度可以增长到200倍。 * 200 / 100
和* 2
存在差异,因此第一个将导致溢出异常,数量减少100倍。例如,如果是字节值,x * 200 / 100
将因x == 2而失败,但只有当x大到128时,* 2
才会失败。
但正如马塞洛·坎托斯指出(long)this._array.Length * 200L / 100L
永远不会溢出所以我的回答可能没多大帮助。
它可以成为ILSpy的“功能”吗?也许在源代码中它只是* 2
。
修改强>
经过进一步的调查后,看起来这个奇怪的代码必须是一些重构的人工制品。我已经检查了它在List&lt;&gt;
中是如何完成的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库是完美的,一切都有意义,但很可能不是每一行代码都是由天才编写的: - )