diff --git a/src/Riok.Mapperly/Descriptors/MappingBuilders/DictionaryMappingBuilder.cs b/src/Riok.Mapperly/Descriptors/MappingBuilders/DictionaryMappingBuilder.cs index c12866fbb4..e2d2a2c30f 100644 --- a/src/Riok.Mapperly/Descriptors/MappingBuilders/DictionaryMappingBuilder.cs +++ b/src/Riok.Mapperly/Descriptors/MappingBuilders/DictionaryMappingBuilder.cs @@ -37,6 +37,9 @@ or CollectionType.IDictionary or CollectionType.IReadOnlyDictionary ) { + if (TryGetFromEnumerable(ctx, keyMapping, valueMapping) is { } toDictionary) + return toDictionary; + var targetDictionarySymbol = ctx.Types.Get(typeof(Dictionary<,>)).Construct(keyMapping.TargetType, valueMapping.TargetType); ctx.ObjectFactories.TryFindObjectFactory(ctx.Source, ctx.Target, out var dictionaryObjectFactory); return new ForEachSetDictionaryMapping( @@ -135,6 +138,34 @@ private static (INewInstanceMapping, INewInstanceMapping)? BuildKeyValueMapping( return (keyMapping, valueMapping); } + private static INewInstanceMapping? TryGetFromEnumerable( + MappingBuilderContext ctx, + INewInstanceMapping keyMapping, + INewInstanceMapping valueMapping + ) + { + if (!keyMapping.IsSynthetic || !valueMapping.IsSynthetic || keyMapping.TargetType.IsNullable()) + return null; + + // use .NET Core 2+ Dictionary constructor if value and key mapping is synthetic + var enumerableType = ctx.Types.Get(typeof(IEnumerable<>)); + var dictionaryType = ctx.Types.Get(typeof(Dictionary<,>)); + + var fromEnumerableCtor = dictionaryType.Constructors.FirstOrDefault( + x => + x.Parameters.Length == 1 + && SymbolEqualityComparer.Default.Equals(((INamedTypeSymbol)x.Parameters[0].Type).ConstructedFrom, enumerableType) + ); + + if (fromEnumerableCtor != null) + { + var constructedDictionary = dictionaryType.Construct(keyMapping.TargetType, valueMapping.TargetType); + return new CtorMapping(ctx.Source, constructedDictionary); + } + + return null; + } + private static INamedTypeSymbol? GetExplicitIndexer(MappingBuilderContext ctx) { if ( diff --git a/test/Riok.Mapperly.Tests/Mapping/DictionaryTest.cs b/test/Riok.Mapperly.Tests/Mapping/DictionaryTest.cs index a9adfc042f..cacc3c986f 100644 --- a/test/Riok.Mapperly.Tests/Mapping/DictionaryTest.cs +++ b/test/Riok.Mapperly.Tests/Mapping/DictionaryTest.cs @@ -24,16 +24,7 @@ public void DictionaryToSameDictionaryDeepCloning() TestHelper .GenerateMapper(source) .Should() - .HaveSingleMethodBody( - """ - var target = new global::System.Collections.Generic.Dictionary(source.Count); - foreach (var item in source) - { - target[item.Key] = item.Value; - } - return target; - """ - ); + .HaveSingleMethodBody("return new global::System.Collections.Generic.Dictionary(source);"); } [Fact] @@ -126,16 +117,7 @@ public void KeyValueEnumerableToIDictionary() TestHelper .GenerateMapper(source) .Should() - .HaveSingleMethodBody( - """ - var target = new global::System.Collections.Generic.Dictionary(); - foreach (var item in source) - { - target[item.Key] = item.Value; - } - return target; - """ - ); + .HaveSingleMethodBody("return new global::System.Collections.Generic.Dictionary(source);"); } [Fact] @@ -145,16 +127,7 @@ public void CustomDictionaryToIDictionary() TestHelper .GenerateMapper(source) .Should() - .HaveSingleMethodBody( - """ - var target = new global::System.Collections.Generic.Dictionary(source.Count); - foreach (var item in source) - { - target[item.Key] = item.Value; - } - return target; - """ - ); + .HaveSingleMethodBody("return new global::System.Collections.Generic.Dictionary(source);"); } [Fact] @@ -164,16 +137,7 @@ public void CustomKeyValueListToIDictionary() TestHelper .GenerateMapper(source) .Should() - .HaveSingleMethodBody( - """ - var target = new global::System.Collections.Generic.Dictionary(source.Count); - foreach (var item in source) - { - target[item.Key] = item.Value; - } - return target; - """ - ); + .HaveSingleMethodBody("return new global::System.Collections.Generic.Dictionary(source);"); } [Fact]