public override void Emit(Type sourceType, Type targetType, CompilationContext context) { context.LoadSource(LoadPurpose.Parameter); context.CurrentType = sourceType; context.EmitCast(typeof(IEnumerable <>).MakeGenericType(_sourceElementType)); context.LoadTarget(LoadPurpose.Parameter); context.CurrentType = targetType; context.EmitCast(typeof(IEnumerable <>).MakeGenericType(_targetElementType)); _invokerBuilder.Emit(context); }
public override void Emit(Type sourceType, Type targetType, CompilationContext context) { var reflectingTargetType = targetType.GetTypeInfo(); context.EmitCast(typeof(IEnumerable <>).MakeGenericType(_sourceElementType)); _invokerBuilder.Emit(context); if (reflectingTargetType.IsGenericType) { var genericTypeDefinition = targetType.GetGenericTypeDefinition(); if (genericTypeDefinition == typeof(IList <>) || genericTypeDefinition == typeof(ICollection <>) || genericTypeDefinition == typeof(IEnumerable <>)) { context.EmitCast(targetType); return; } } else if (targetType.IsArray) { context.EmitCast(targetType); return; } if (targetType.IsEnumerable(out var targetElementType)) { var constructor = reflectingTargetType.GetConstructor(new[] { typeof(IEnumerable <>).MakeGenericType(targetElementType) }) ?? reflectingTargetType.GetConstructor(new[] { typeof(IList <>).MakeGenericType(targetElementType) }) ?? reflectingTargetType.GetConstructor(new[] { typeof(ICollection <>).MakeGenericType(targetElementType) }) ?? reflectingTargetType.GetConstructor(new[] { targetElementType.MakeArrayType() }); if (constructor != null) { context.EmitCast(constructor.GetParameters()[0].ParameterType); context.Emit(OpCodes.Newobj, constructor); context.CurrentType = targetType; } else { var defaultConstructor = reflectingTargetType.GetConstructor(Type.EmptyTypes); var addMethod = reflectingTargetType.GetMethods(BindingFlags.Instance | BindingFlags.Public).Where(method => { if (method.Name != "Add") { return(false); } var parameters = method.GetParameters(); return(parameters.Length == 1 && parameters[0].ParameterType == targetElementType); }).FirstOrDefault(); if (defaultConstructor != null && addMethod != null) { var targetArrayType = targetElementType.MakeArrayType(); var targetArray = context.DeclareLocal(targetArrayType); var targetInstance = context.DeclareLocal(targetType); var index = context.DeclareLocal(typeof(int)); context.EmitCast(targetArrayType); context.Emit(OpCodes.Stloc, targetArray); context.Emit(OpCodes.Newobj, defaultConstructor); context.Emit(OpCodes.Stloc, targetInstance); // var i = 0; context.Emit(OpCodes.Ldc_I4_0); context.Emit(OpCodes.Stloc, index); var labelEnd = context.DefineLabel(); context.Emit(OpCodes.Br_S, labelEnd); var labelStart = context.DefineLabel(); context.MakeLabel(labelStart); // target.Add(array[i]); context.Emit(OpCodes.Ldloc, targetInstance); context.Emit(OpCodes.Ldloc, targetArray); context.Emit(OpCodes.Ldloc, index); var targetElementTypeInfo = targetElementType.GetTypeInfo(); if (targetElementTypeInfo.IsValueType && !targetElementTypeInfo.IsPrimitive) { context.Emit(OpCodes.Ldelema, targetElementType); } else { context.Emit(OpCodes.Ldelem, targetElementType); } context.EmitCall(addMethod); // i++ context.Emit(OpCodes.Ldloc, index); context.Emit(OpCodes.Ldc_I4_1); context.Emit(OpCodes.Add); context.Emit(OpCodes.Stloc, index); context.MakeLabel(labelEnd); context.Emit(OpCodes.Ldloc, index); context.Emit(OpCodes.Ldloc, targetArray); context.Emit(OpCodes.Ldlen); context.Emit(OpCodes.Conv_I4); context.Emit(OpCodes.Blt_S, labelStart); context.Emit(OpCodes.Ldloc, targetInstance); context.CurrentType = targetType; } } } }