-
-
Notifications
You must be signed in to change notification settings - Fork 206
Description
Is your feature request related to a problem? Please describe.
I noticed various null checks in the generated code that didn't need to be there or could be reduced from two checks to a single check.
I'm aware of #1614 but since that seems to (only?) be about correctness I created a separate issue for these examples.
Describe the solution you'd like
In each example below the generated code performs more null checks that necessary.
Ideally we emit C# code that performs the minimum number of null checks.
Example 1
source is null checked twice, explicitly with == null and implicitly through .Value.
[Mapper]
public partial class A
{
public partial int? Map(int? source);
}Actual
// <auto-generated />
#nullable enable
public partial class A
{
[global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "4.2.1.0")]
public partial int? Map(int? source)
{
return source == null ? default(int?) : source.Value;
}
}Expected
// <auto-generated />
#nullable enable
public partial class A
{
[global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "4.2.1.0")]
public partial int? Map(int? source)
{
return source;
}
}Example 2
Like in example 1 source is null checked twice, but to extract the value from decimal? we can use Nullable<T>.GetValueOrDefault() to save the null check inside Nullable<T>.Value.
[Mapper]
public partial class E
{
public partial int? Map(decimal? source);
}Actual
// <auto-generated />
#nullable enable
public partial class E
{
[global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "4.2.1.0")]
public partial int? Map(decimal? source)
{
return source == null ? default(int?) : (int)source.Value;
}
}Expected
// <auto-generated />
#nullable enable
public partial class E
{
[global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "4.2.1.0")]
public partial int? Map(decimal? source)
{
return source == null ? default(int?) : (int)source.GetValueOrDefault();
}
}Example 3
Each item of source is null checked.
#nullable enable
[Mapper(UseDeepCloning = true)]
public partial class B
{
public partial List<string?> Map(List<string?> source);
}
#nullable restoreActual
// <auto-generated />
#nullable enable
public partial class B
{
[global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "4.2.1.0")]
public partial global::System.Collections.Generic.List<string?> Map(global::System.Collections.Generic.List<string?> source)
{
var target = new global::System.Collections.Generic.List<string?>(source.Count);
foreach (var item in source)
{
target.Add(item == null ? default : item);
}
return target;
}
}Expected
// <auto-generated />
#nullable enable
public partial class B
{
[global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "4.2.1.0")]
public partial global::System.Collections.Generic.List<string?> Map(global::System.Collections.Generic.List<string?> source)
{
var target = new global::System.Collections.Generic.List<string?>(source.Count);
foreach (var item in source)
{
target.Add(item);
}
return target;
}
}Example 4
Each item of source is null checked.
I don't know if it's related but furthermore I would expect that we just called Enumerable.ToList<int?>(IEnumerable<int?> source)
like a generated mapper from List<int> to List<int> does.
[Mapper(UseDeepCloning = true)]
public partial class C
{
public partial List<int?> Map(List<int?> source);
}Actual
// <auto-generated />
#nullable enable
public partial class C
{
[global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "4.2.1.0")]
public partial global::System.Collections.Generic.List<int?> Map(global::System.Collections.Generic.List<int?> source)
{
return global::System.Linq.Enumerable.ToList(source);
}
}Expected
// <auto-generated />
#nullable enable
public partial class C
{
[global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "4.2.1.0")]
public partial global::System.Collections.Generic.List<int?> Map(global::System.Collections.Generic.List<int?> source)
{
var target = new global::System.Collections.Generic.List<int?>(source.Count);
foreach (var item in source)
{
target.Add(item);
}
return target;
}
}Example 5
Each item.Key of source in MapToDictionaryOfStringAndInt32 is null checked.
#nullable disable
[Mapper(UseDeepCloning = true)]
public partial class D
{
public partial MyClass Map(MyClass source);
}
public class MyClass
{
public Dictionary<string, int> MyProperty { get; set; }
}
#nullable enableActual
// <auto-generated />
#nullable enable
public partial class D
{
[global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "4.2.1.0")]
[return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull(nameof(source))]
public partial global::MyClass? Map(global::MyClass? source)
{
if (source == null)
return default;
var target = new global::MyClass();
if (source.MyProperty != null)
{
target.MyProperty = MapToDictionaryOfStringAndInt32(source.MyProperty);
}
else
{
target.MyProperty = null;
}
return target;
}
[global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "4.2.1.0")]
private global::System.Collections.Generic.Dictionary<string?, int> MapToDictionaryOfStringAndInt32(global::System.Collections.Generic.IReadOnlyDictionary<string?, int> source)
{
var target = new global::System.Collections.Generic.Dictionary<string?, int>(source.Count);
foreach (var item in source)
{
target[item.Key == null ? default : item.Key] = item.Value;
}
return target;
}
}Expected
// <auto-generated />
#nullable enable
public partial class D
{
[global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "4.2.1.0")]
[return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull(nameof(source))]
public partial global::MyClass? Map(global::MyClass? source)
{
if (source == null)
return default;
var target = new global::MyClass();
if (source.MyProperty != null)
{
target.MyProperty = MapToDictionaryOfStringAndInt32(source.MyProperty);
}
else
{
target.MyProperty = null;
}
return target;
}
[global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "4.2.1.0")]
private global::System.Collections.Generic.Dictionary<string?, int> MapToDictionaryOfStringAndInt32(global::System.Collections.Generic.IReadOnlyDictionary<string?, int> source)
{
var target = new global::System.Collections.Generic.Dictionary<string?, int>(source.Count);
foreach (var item in source)
{
target[item.Key] = item.Value;
}
return target;
}
}