-
Notifications
You must be signed in to change notification settings - Fork 11
/
PropMapper.cs
82 lines (69 loc) · 2.95 KB
/
PropMapper.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace Jitbit.Utils
{
//clones object public properties to another object
//uses Expressions (compiled and saved to static) - faster than Reflection
//(compilation happens with every new generic type call cause it's a new static class each time)
public static class PropMapper<TInput, TOutput>
{
private static readonly Func<TInput, TOutput> _cloner;
private static readonly Action<TInput, TOutput> _copier;
private static readonly IEnumerable<PropertyInfo> _sourceProperties;
private static readonly IEnumerable<PropertyInfo> _destinationProperties;
static PropMapper()
{
_destinationProperties = typeof(TOutput)
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(prop => prop.CanWrite);
_sourceProperties = typeof(TInput)
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(prop => prop.CanRead);
_cloner = CreateCloner();
_copier = CreateCopier();
}
private static Func<TInput, TOutput> CreateCloner()
{
//check if type has parameterless constructor - just in case
if (typeof(TOutput).GetConstructor(Type.EmptyTypes) == null) return ((x) => default(TOutput));
var input = Expression.Parameter(typeof(TInput), "input");
// For each property that exists in the destination object, is there a property with the same name in the source object?
var memberBindings = _sourceProperties.Join(_destinationProperties,
sourceProperty => sourceProperty.Name,
destinationProperty => destinationProperty.Name,
(sourceProperty, destinationProperty) =>
(MemberBinding)Expression.Bind(destinationProperty,
Expression.Property(input, sourceProperty)));
var body = Expression.MemberInit(Expression.New(typeof(TOutput)), memberBindings);
var lambda = Expression.Lambda<Func<TInput, TOutput>>(body, input);
return lambda.Compile();
}
private static Action<TInput, TOutput> CreateCopier()
{
var input = Expression.Parameter(typeof(TInput), "input");
var output = Expression.Parameter(typeof(TOutput), "output");
// For each property that exists in the destination object, is there a property with the same name in the source object?
var memberAssignments = _sourceProperties.Join(_destinationProperties,
sourceProperty => sourceProperty.Name,
destinationProperty => destinationProperty.Name,
(sourceProperty, destinationProperty) =>
Expression.Assign(Expression.Property(output, destinationProperty),
Expression.Property(input, sourceProperty)));
var body = Expression.Block(memberAssignments);
var lambda = Expression.Lambda<Action<TInput, TOutput>>(body, input, output);
return lambda.Compile();
}
public static TOutput From(TInput input)
{
if (input == null) return default(TOutput);
return _cloner(input);
}
public static void CopyTo(TInput input, TOutput output)
{
_copier(input, output);
}
}
}