65.9K
CodeProject 正在变化。 阅读更多。
Home

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

2009年5月17日

CPOL

2分钟阅读

viewsIcon

33728

downloadIcon

90

在仍使用非泛型 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 万次迭代基准测试并使用高分辨率计时器后,我发现使用 ArrayListVariantList 插入和检索项目所花费的时间之间没有太大差异(只有几毫秒)。 并且我会诚实地告诉你,在重复基准测试时,VariantList 实际上比 ArrayList 慢一点。

无论如何,如果您想使用此代码,请执行一些基准测试,看看它是否对您有任何优势。 此外,如果您对此文章有任何意见、建议、批评等,请随时分享。

© . All rights reserved.