ConvertEnumerator——对数据集进行迭代转换
在程序中需要遇到这样的情况,已有一个数据集A,但需要一个这样的可迭代数据集B:其中数据均由A中相应位置处的数据按指定规则转换得到。一个简单的例子是假定已经有一个人员信息(Person类型)列表,但我们希望得到格式为“名字(性别)”的字符串的可迭代结构。
对这种情况,一般情况下只需在使用的地方迭代原列表,然后根据Person对象生成格式化字符串即可。
但也并非总能如此处理,例如我不希望这个格式化的逻辑散布在各个需要格式化字符串的地方,若格式化过程比较复杂或将来输出格式需要改变,则这将会比较麻烦。因此,我希望提供人员信息列表的模块能够再提供一个格式化后字符串列表的方法,使格式化的逻辑集中在此。
但就是这样一个方法让我踌躇了好一阵子,我有两条路:一是每次调用方法时执行一次转换,生成一个格式化字符串的列表返回给用户;这种方法的劣势是每次调用方法都必须创建一个列表和执行一次转换,内存开销比较大。二是在模块中维护一个与人员信息列表同步的格式化字符串列表,每次调用时直接返回此列表;此方法的优点是不必每次调用时都创建列表和进行一次转换,劣势是多一了项同步的工作。而且假如人员列表变化比较频繁的话,缓存格式化列表的机制的效果就不会很明显,在方法调用不频繁的情况下甚至可能会使效率更低。
在我遇到的几个场景中,由于恰好原列表变化比较频繁,而调用却不甚多。所以我都采用了第一种方法。但是,我依然对需要创建一个列表的内存开销耿耿于怀。如果这个列表很大的话,即便创建一个等容量的数组也让人心疼。
为了解决这个问题,我写了一个扩展的迭代器。此迭代器映射到一个原始的可迭代结构,并且其Current值是原始迭代器Currnet经过转换后的值。有了这样一个迭代器类型后,我就可以在上面那个方法中根据人员信息列表和一个包含转换规则的Converter委托初始化一个迭代器并将其返回。这样就避免了创建容量未知的数组,其代价则是每迭代一个数据都要进行转换,而且执行委托的效率也将比直接执行代码的效率稍低。
这是一种以时间换空间的做法,也是一个以稳定效率代替不稳定效率的方法。而在一些界面相关操作中,即使稍低的效率也难以被用户感觉到。另外,在一些集合数据转换的处理中,利用此迭代器也可简化写法。
示例:
int[] ints = new int[] { 3, 4, 2, 6, 7, 2, 5 }; List<string> strs = new List<string>(new ConvertEnumeratorProvider<string>(ints, x => x.ToString())); label1.Text = StringAssembler.StringSplice(",", strs);
迭代器:
/// <summary> /// 转换迭代器提供者:用于获取ConvertEnumerator,该迭代器可对数据源中项进行转换后枚举;此类为ConvertEnumeratorProvider<TOutput>的泛型版本 /// </summary> /// <typeparam name="TInput">源数据中数据类型</typeparam> /// <typeparam name="TOutput">转换后的数据类型</typeparam> public class ConvertEnumeratorProvider<TInput, TOutput> : IEnumerable<TOutput>, IDisposable, IEnumerable { #region Fields private IEnumerable<TInput> Source; private Converter<TInput, TOutput> Converter; #endregion #region Methods public ConvertEnumeratorProvider(IEnumerable<TInput> source, Converter<TInput, TOutput> converter) { this.Source = source; this.Converter = converter; } public IEnumerator<TOutput> GetEnumerator() { return new ConvertEnumerator<TInput, TOutput>(Source, Converter); } IEnumerator IEnumerable.GetEnumerator() { return new ConvertEnumerator<TInput, TOutput>(Source, Converter); } public void Dispose() { } #endregion } /// <summary> /// 转换迭代器:对源数据集进行转换输出;此类为ConvertEnumerator<TOutput>的泛型版本 /// </summary> /// <typeparam name="TIntput">数据源中数据类型</typeparam> /// <typeparam name="TOutput">转换后的数据类型</typeparam> public class ConvertEnumerator<TInput,TOutput> : IEnumerator<TOutput>,IEnumerator, IDisposable { #region Fields private IEnumerator<TInput> Source; private Converter<TInput,TOutput> Converter; #endregion #region Properties /// <summary> /// 当前值 /// </summary> public TOutput Current { get { return Converter(Source.Current); } } /// <summary> /// 当前值 /// </summary> object IEnumerator.Current { get { return Converter(Source.Current); } } #endregion #region Methods public ConvertEnumerator(IEnumerable<TInput> source, Converter<TInput,TOutput> converter) { if (object.ReferenceEquals(null, source) || object.ReferenceEquals(null, converter)) { throw new ArgumentNullException(); } this.Source = source.GetEnumerator(); this.Converter = converter; } public ConvertEnumerator(IEnumerator<TInput> source, Converter<TInput, TOutput> converter) { if (object.ReferenceEquals(null, source) || object.ReferenceEquals(null, converter)) { throw new ArgumentNullException(); } this.Source = source; this.Converter = converter; } public bool MoveNext() { return Source.MoveNext(); } public void Reset() { Source.Reset(); } /// <summary> /// 释放资源 /// </summary> public void Dispose() { } #endregion }