Lists and Arrays
You don’t need to define separate mapper for collection or array type, MappingGenerator creates them for you.
Source collection types:
IEnumerable<T>ICollection<T>IList<T>Collection<T>HashSet<T>List<T>Arrays
public class Source
{
public string Text { get; set; }
}
public class Destination
{
public string Text { get; set; }
}
[MappingGenerator(typeof(Source), typeof(Destination))]
public partial class Mapper
{ }
Generated code (removed redundant parts and added comments for brevity):
partial class Mapper : IMapper<Source, Destination>
{
public Destination Map(Source source)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
var result = CreateDestination(source);
result.Text = source.Text;
return result;
}
}
Usage:
var source = new Source[] { ... };
var mapper = new Mapper(source);
IEnumerable<Destination> enumerable = mapper.Map(source);
Destination[] array = mapper.ToArray(source);
List<Destination> list = mapper.ToList(source);
IList<Destination> ilist = mapper.ToList(source);
Collection<Destination> collection = mapper.ToCollection(source);
HashSet<Destination> collection = mapper.ToHashSet(source);
Nested collections mappings behavior
When mapping enumerable properties the following rules apply:
If source is
nullMappingGenerator will map empty collection.If destination is read-only property:
If destination implements
ICollectionand isnullnothing will happen.If destination implements
ICollectionand is notnull, it’s cleared and will be repopulated with mapped elements of source.
If destination is read-write property:
If destination implements
ICollectionand isnullit will be replaced with newList<T>instance with mapped elements of source.If destination implements
ICollectionand is notnull, it’s cleared and will be repopulated with mapped elements of source.If destination is
IEnumerableit will be replaced with newList<T>instance with mapped elements of source.
Constructor parameters and init-only properties always initialized new
List<T>instance with mapped elements of source.
The following sample demonstrates more MappingGenerator features:
public class A { }
public class B { }
public class C { }
public class D : C { }
public class Source
{
public IEnumerable<A> Nested { get; set; } = new List<A>();
public IEnumerable<C> Simple { get; set; } = new List<C>();
public IEnumerable<C> ReadOnly { get; set; } = new List<C>();
public IEnumerable<A> Collection { get; set; } = new List<A>();
public HashSet<A> HashSet { get; set; } = new HashSet<A>();
public IEnumerable<long> ExplicitCast { get; set; } = new List<long>();
public IEnumerable<D> Covariation { get; set; } = new List<D>();
}
public class Destination
{
public IEnumerable<B> Nested { get; set; } = default!;
public IEnumerable<C> Simple { get; set; } = default!;
public List<C> ReadOnly { get; } = default!;
public ICollection<B> Collection { get; set; } = default!;
public HashSet<A> HashSet { get; set; } = new HashSet<A>();
public ICollection<int> ExplicitCast { get; set; } = default!;
public ICollection<C> Covariation { get; set; } = default!;
}
[MappingGenerator(typeof(A), typeof(B))]
public partial class ABMapper
{
}
[MappingGenerator(typeof(Source), typeof(Destination))]
public partial class Mapper
{
}
Generated code (removed redundant parts and added comments for brevity):
// Implementation omitted.
partial class ABMapper
{}
partial class Mapper : IMapper<Source, Destination>
{
private IMapper<A, B> aBMapper;
public Mapper(IMapper<A, B> aBMapper)
{
if (aBMapper == null)
throw new ArgumentNullException(nameof(aBMapper));
this.aBMapper = aBMapper;
}
public Destination Map(Source source)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
Destination CreateDestination()
{
return new Destination()
{};
}
var result = CreateDestination();
// Reused existing mapper A => B.
result.Nested = this.aBMapper.ToList(source.Nested);
// Destination is writable IEnumerable.
result.Simple = CollectionsHelper.CopyToNewList<C, List<C>>(source.Simple);
// Destination is read-only ICollection.
CollectionsHelper.CopyTo<C>(source.ReadOnly, result.ReadOnly);
// Destination is read-write ICollection.
if (result.Collection == null)
result.Collection = CollectionsHelper.CopyToNewList<A, B>(source.Collection, p => aBMapper.Map(p));
else
CollectionsHelper.CopyTo<A, B>(source.Collection, result.Collection, p => aBMapper.Map(p));
// Destination is read-write HashSet.
if (result.HashSet == null)
result.HashSet = CollectionsHelper.CopyToNewHashSet<A>(source.HashSet);
else
CollectionsHelper.CopyTo<A>(source.HashSet, result.HashSet);
// Destination required explicit cast.
if (result.ExplicitCast == null)
result.ExplicitCast = CollectionsHelper.CopyToNewList<long, int>(source.ExplicitCast, static p => (int)p);
else
CollectionsHelper.CopyTo<long, int>(source.ExplicitCast, result.ExplicitCast, static p => (int)p);
// IEnumerable<D> has implicit cast to ICollection<C> because D is child class of C.
if (result.Covariation == null)
result.Covariation = CollectionsHelper.CopyToNewList<C>(source.Covariation);
else
CollectionsHelper.CopyTo<C>(source.Covariation, result.Covariation);
return result;
}
}