在.NET中,IEnumerable<T>
和IEnumerable
接口用于支持集合的迭代。要实现这些接口,需要提供两个GetEnumerator()
方法:
IEnumerator<T> GetEnumerator()
IEnumerator IEnumerable.GetEnumerator()
public class MyCollection<T> : IEnumerable<T>
{
private readonly List<T> _items = new List<T>();
// 泛型版本
public IEnumerator<T> GetEnumerator()
{
return _items.GetEnumerator();
}
// 非泛型版本(显式实现)
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator(); // 调用泛型版本
}
}
public class MyCollection<T> : IEnumerable<T>
{
private readonly List<T> _items = new List<T>();
public IEnumerator<T> GetEnumerator()
{
foreach (var item in _items)
{
yield return item;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
public static class EnumerableGenerator
{
public static Type CreateEnumerableType(Type itemType)
{
var assemblyName = new AssemblyName("DynamicEnumerableAssembly");
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
var typeBuilder = moduleBuilder.DefineType(
$"DynamicEnumerable`1[{itemType.Name}]",
TypeAttributes.Public,
typeof(object),
new[] { typeof(IEnumerable<>).MakeGenericType(itemType), typeof(IEnumerable) });
// 添加字段存储集合数据
var fieldBuilder = typeBuilder.DefineField(
"_items",
typeof(List<>).MakeGenericType(itemType),
FieldAttributes.Private);
// 添加构造函数
var ctorBuilder = typeBuilder.DefineConstructor(
MethodAttributes.Public,
CallingConventions.Standard,
new[] { typeof(IEnumerable<>).MakeGenericType(itemType) });
var ctorIl = ctorBuilder.GetILGenerator();
ctorIl.Emit(OpCodes.Ldarg_0);
ctorIl.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes));
ctorIl.Emit(OpCodes.Ldarg_0);
ctorIl.Emit(OpCodes.Ldarg_1);
ctorIl.Emit(OpCodes.Newobj, typeof(List<>).MakeGenericType(itemType).GetConstructor(new[] { typeof(IEnumerable<>).MakeGenericType(itemType) }));
ctorIl.Emit(OpCodes.Stfld, fieldBuilder);
ctorIl.Emit(OpCodes.Ret);
// 实现泛型 GetEnumerator
var methodBuilder = typeBuilder.DefineMethod(
"GetEnumerator",
MethodAttributes.Public | MethodAttributes.Virtual,
typeof(IEnumerator<>).MakeGenericType(itemType),
Type.EmptyTypes);
var methodIl = methodBuilder.GetILGenerator();
methodIl.Emit(OpCodes.Ldarg_0);
methodIl.Emit(OpCodes.Ldfld, fieldBuilder);
methodIl.Emit(OpCodes.Callvirt, typeof(List<>).MakeGenericType(itemType).GetMethod("GetEnumerator"));
methodIl.Emit(OpCodes.Ret);
// 实现接口的泛型 GetEnumerator
typeBuilder.DefineMethodOverride(
methodBuilder,
typeof(IEnumerable<>).MakeGenericType(itemType).GetMethod("GetEnumerator"));
// 实现非泛型 GetEnumerator
var nonGenericMethodBuilder = typeBuilder.DefineMethod(
"System.Collections.IEnumerable.GetEnumerator",
MethodAttributes.Private | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Final,
typeof(IEnumerator),
Type.EmptyTypes);
var nonGenericMethodIl = nonGenericMethodBuilder.GetILGenerator();
nonGenericMethodIl.Emit(OpCodes.Ldarg_0);
nonGenericMethodIl.Emit(OpCodes.Call, methodBuilder);
nonGenericMethodIl.Emit(OpCodes.Ret);
// 实现接口的非泛型 GetEnumerator
typeBuilder.DefineMethodOverride(
nonGenericMethodBuilder,
typeof(IEnumerable).GetMethod("GetEnumerator"));
return typeBuilder.CreateType();
}
}
// 创建动态类型
var dynamicType = EnumerableGenerator.CreateEnumerableType(typeof(string));
// 创建实例
var instance = Activator.CreateInstance(dynamicType, new object[] { new List<string> { "a", "b", "c" } });
// 使用foreach迭代
foreach (var item in (IEnumerable)instance)
{
Console.WriteLine(item);
}
问题1:为什么需要实现两个GetEnumerator方法?
原因:.NET 1.0只有非泛型IEnumerable,.NET 2.0引入了泛型IEnumerable<T>。为了向后兼容,需要同时实现两个版本。
解决方案:总是先实现泛型版本,然后让非泛型版本调用泛型版本。
问题2:动态生成的类型性能如何?
原因:首次生成类型会有开销,但之后使用与手动编写的类性能相同。
解决方案:考虑缓存生成的类型以减少运行时开销。
问题3:如何处理自定义迭代逻辑?
原因:动态生成简单委托给集合的枚举器,但有时需要自定义逻辑。
解决方案:可以在生成代码中添加自定义迭代逻辑,或使用迭代器方法(yield return)模式。