无需装箱和拆箱的非泛型 IList 实现





2.00/5 (4投票s)
在仍使用非泛型 IList 实现的同时,一种避免装箱和拆箱的简单变通方法。
更新 (2009 年 5 月 18 日):本文的标题给一些 CodeProject 会员带来了困惑,解释是我的本意不是完全避免 .NET 泛型,而是展示一个“非泛型 System.Collections.IList
接口”的实现,并且没有装箱和拆箱。
在撰写这篇文章时,我假设您了解 .NET 上下文中装箱/拆箱的含义,如果您不了解,请阅读这篇 MSDN 文章以了解更多信息。
在 IList
接口的默认实现中,例如 ArrayList
,当您将 ValueType
项目 Add()
到列表中时,该值会被装箱到 Object
中。 同样,当从列表中检索 ValueType
元素时,会发生拆箱,并且必须执行显式转换。
现在,根据 MSDN 文档,这可能比简单的引用赋值花费的时间长达 20 倍,并且转换花费的时间是赋值的四倍。
下面是我自己的实现,它消除了装箱/拆箱问题,同时仍然使用同样老式的非泛型 IList
class VariantList : VariantListBase
{
#region Methods
public int Append<T>(T value) where T : struct {
return (this as IList).Add(new T[1] { value });
}
public T GetAt<T>(int index) where T : struct {
T[] value = (T[])(this[index]);
return value[0];
}
#endregion
}
VariantListBase
是一个 abstract
类,它实现 IList
,其结构类似于以下内容
abstract class VariantListBase : System.Collections.IList
{
private System.Collections.IList innerList = new System.Collections.ArrayList();
/*********
* IList, ICollection and IEnumerable method implementations
* simply forward the call to this.innerList
*********/
}
那么,VariantList
如何避免装箱/拆箱? 它通过使用数组来实现这一点。
请记住,任何类型的数组都派生自 System.Array
基类,而基类本身就是引用类型。 这正是 Append<T>(T value)
方法中所做的。 当 Append<T>()
用于将 ValueType
元素添加到列表时,将创建一个新的相同类型 T
的单元素数组并添加,从而完全避免了装箱的需要。 同样,当使用 GetAt<T>(int index)
方法检索值时,它将在不执行任何拆箱过程的情况下检索。
但事实是:在执行了 1000 万次迭代基准测试并使用高分辨率计时器后,我发现使用 ArrayList
和 VariantList
插入和检索项目所花费的时间之间没有太大差异(只有几毫秒)。 并且我会诚实地告诉你,在重复基准测试时,VariantList
实际上比 ArrayList
慢一点。
无论如何,如果您想使用此代码,请执行一些基准测试,看看它是否对您有任何优势。 此外,如果您对此文章有任何意见、建议、批评等,请随时分享。