public void EmittedAppendToStringBuilderIsValid(Type emitterType, Type valueType, object testValue, string expectedResult) { if (valueType == typeof(decimal)) { testValue = (decimal)(double)testValue; } else if (valueType == typeof(decimal?)) { testValue = (decimal?)(double?)testValue; } IConverterEmitter emitter = (IConverterEmitter)Activator.CreateInstance(emitterType) !; DynamicMethod serialize = new DynamicMethod("Serialize", typeof(string), new Type[] { valueType, typeof(IFormatProvider), typeof(char) }, typeof(NumberConverterTests)); LocalBuilder? local = null; LocalBuilder? secondaryLocal = null; serialize.GetILGenerator() .Emit(gen => { if (valueType == typeof(float) || valueType == typeof(double) || valueType == typeof(decimal)) { gen.DeclareLocal(valueType, out local); } else if (Nullable.GetUnderlyingType(valueType) is Type underlyingType) { if (underlyingType == typeof(float) || underlyingType == typeof(double) || underlyingType == typeof(decimal)) { gen.DeclareLocal(typeof(Nullable <>).MakeGenericType(underlyingType), out local); gen.DeclareLocal(valueType, out secondaryLocal); } else { gen.DeclareLocal(typeof(Nullable <>).MakeGenericType(underlyingType), out local); } } }) .Newobj <StringBuilder>() .Ldarg_0() .Emit(gen => emitter.EmitAppendToStringBuilder(gen, local, secondaryLocal, null)) .Callvirt <StringBuilder>("ToString") .Ret(); string serialized = (string)serialize.Invoke(null, new[] { testValue, CultureInfo.InvariantCulture, ',' }) !; serialized.Should().Be(expectedResult); }
private static void DefineSerializeItem <T>(ILGenerator gen) { PropertyInfo[] properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance); Dictionary <Type, LocalBuilder> localByType = new Dictionary <Type, LocalBuilder>(); gen .Ldarg_3() .Emit(gen => { bool firstProperty = true; foreach (PropertyInfo property in properties) { if (!property.CanRead) { throw new CsvTypeException(typeof(T), property.Name, "Property doesn't have a public getter."); } CsvColumnAttribute?columnAttribute = property.GetCustomAttribute <CsvColumnAttribute>(); gen .Emit(gen => firstProperty switch { false => gen .Ldarg_2() .Callvirt <StringBuilder>("Append", typeof(char)), _ => gen }) .Emit(gen => gen .Ldarg(4) .Callvirt(property.GetGetMethod() !) .Emit(gen => { IConverterEmitter converterEmitter = ConverterFactory.GetOrCreateEmitter(property.PropertyType); ConverterEmitterAttribute converterEmitterAttribute = converterEmitter.GetType().GetMethod("EmitAppendToStringBuilder") !.GetCustomAttribute <ConverterEmitterAttribute>() !; LocalBuilder?local = null; LocalBuilder?secondaryLocal = null; if (converterEmitterAttribute.NullableOfGenericParameterIsPrimaryLocalType && converterEmitter.GetType().IsGenericType) { Type genericParameter = converterEmitter.GetType().GetGenericArguments()[0]; Type localType = typeof(Nullable <>).MakeGenericType(genericParameter); if (!localByType.TryGetValue(localType, out local)) { local = gen.DeclareLocal(localType); localByType.Add(localType, local); } } else if (converterEmitterAttribute.GenericParameterIsPrimaryLocalType && converterEmitter.GetType().IsGenericType) { Type localType = converterEmitter.GetType().GetGenericArguments()[0]; if (!localByType.TryGetValue(localType, out local)) { local = gen.DeclareLocal(localType); localByType.Add(localType, local); } } else if (converterEmitterAttribute.PrimaryLocalType is Type localType) { if (!localByType.TryGetValue(localType, out local)) { local = gen.DeclareLocal(localType); localByType.Add(localType, local); } } if (converterEmitterAttribute.SecondaryLocalType is Type secondaryLocalType) { if (!localByType.TryGetValue(secondaryLocalType, out secondaryLocal)) { secondaryLocal = gen.DeclareLocal(secondaryLocalType); localByType.Add(secondaryLocalType, secondaryLocal); } } converterEmitter.EmitAppendToStringBuilder(gen, local, secondaryLocal, columnAttribute); }) ); firstProperty = false; }