1
- using System . Linq . Expressions ;
2
- using System ;
1
+ using System ;
3
2
using System . Collections ;
4
3
using System . Collections . Generic ;
4
+ using System . Diagnostics . CodeAnalysis ;
5
5
using System . Linq ;
6
+ using System . Linq . Expressions ;
6
7
using System . Reflection ;
7
8
8
9
namespace Mapster . Utils
@@ -19,32 +20,112 @@ public static Expression Assign(Expression left, Expression right)
19
20
20
21
public static Expression PropertyOrFieldPath ( Expression expr , string path )
21
22
{
22
- var props = path . Split ( '.' ) ;
23
- return props . Aggregate ( expr , PropertyOrField ) ;
23
+ Expression current = expr ;
24
+ string [ ] props = path . Split ( '.' ) ;
25
+
26
+ for ( int i = 0 ; i < props . Length ; i ++ )
27
+ {
28
+ if ( IsDictionaryKey ( current , props [ i ] , out Expression ? next ) )
29
+ {
30
+ current = next ;
31
+ continue ;
32
+ }
33
+
34
+ if ( IsPropertyOrField ( current , props [ i ] , out next ) )
35
+ {
36
+ current = next ;
37
+ continue ;
38
+ }
39
+
40
+ // For dynamically built types, it is possible to have periods in the property name.
41
+ // Rejoin an incrementing number of parts with periods to try and find a property match.
42
+ if ( IsPropertyOrFieldPathWithPeriods ( current , props [ i ..] , out next , out int combinationLength ) )
43
+ {
44
+ current = next ;
45
+ i += combinationLength - 1 ;
46
+ continue ;
47
+ }
48
+
49
+ throw new ArgumentException ( $ "'{ props [ i ] } ' is not a member of type '{ current . Type . FullName } '", nameof ( path ) ) ;
50
+ }
51
+
52
+ return current ;
24
53
}
25
54
26
- private static Expression PropertyOrField ( Expression expr , string prop )
55
+ private static bool IsPropertyOrFieldPathWithPeriods ( Expression expr , string [ ] path , [ NotNullWhen ( true ) ] out Expression ? propExpr , out int combinationLength )
56
+ {
57
+ if ( path . Length < 2 )
58
+ {
59
+ propExpr = null ;
60
+ combinationLength = 0 ;
61
+ return false ;
62
+ }
63
+
64
+ for ( int count = 2 ; count <= path . Length ; count ++ )
65
+ {
66
+ string prop = string . Join ( '.' , path [ ..count ] ) ;
67
+ if ( IsPropertyOrField ( expr , prop , out propExpr ) )
68
+ {
69
+ combinationLength = count ;
70
+ return true ;
71
+ }
72
+ }
73
+
74
+ propExpr = null ;
75
+ combinationLength = 0 ;
76
+ return false ;
77
+ }
78
+
79
+ private static bool IsDictionaryKey ( Expression expr , string prop , [ NotNullWhen ( true ) ] out Expression ? propExpr )
27
80
{
28
81
var type = expr . Type ;
29
82
var dictType = type . GetDictionaryType ( ) ;
30
- if ( dictType ? . GetGenericArguments ( ) [ 0 ] == typeof ( string ) )
31
- {
32
- var method = typeof ( MapsterHelper ) . GetMethods ( )
33
- . First ( m => m . Name == nameof ( MapsterHelper . GetValueOrDefault ) && m . GetParameters ( ) [ 0 ] . ParameterType . Name == dictType . Name )
34
- . MakeGenericMethod ( dictType . GetGenericArguments ( ) ) ;
35
83
36
- return Expression . Call ( method , expr . To ( type ) , Expression . Constant ( prop ) ) ;
84
+ if ( dictType ? . GetGenericArguments ( ) [ 0 ] != typeof ( string ) )
85
+ {
86
+ propExpr = null ;
87
+ return false ;
37
88
}
38
89
90
+ var method = typeof ( MapsterHelper ) . GetMethods ( )
91
+ . First ( m => m . Name == nameof ( MapsterHelper . GetValueOrDefault ) && m . GetParameters ( ) [ 0 ] . ParameterType . Name == dictType . Name )
92
+ . MakeGenericMethod ( dictType . GetGenericArguments ( ) ) ;
93
+
94
+ propExpr = Expression . Call ( method , expr . To ( type ) , Expression . Constant ( prop ) ) ;
95
+ return true ;
96
+ }
97
+
98
+ private static bool IsPropertyOrField ( Expression expr , string prop , [ NotNullWhen ( true ) ] out Expression ? propExpr )
99
+ {
100
+ Type type = expr . Type ;
101
+
39
102
if ( type . GetTypeInfo ( ) . IsInterface )
40
103
{
41
104
var allTypes = type . GetAllInterfaces ( ) ;
42
105
var flags = BindingFlags . Public | BindingFlags . NonPublic | BindingFlags . Instance ;
43
106
var interfaceType = allTypes . FirstOrDefault ( it => it . GetProperty ( prop , flags ) != null || it . GetField ( prop , flags ) != null ) ;
44
107
if ( interfaceType != null )
108
+ {
45
109
expr = Expression . Convert ( expr , interfaceType ) ;
110
+ type = expr . Type ;
111
+ }
46
112
}
47
- return Expression . PropertyOrField ( expr , prop ) ;
113
+
114
+ MemberInfo ? propertyOrField = type
115
+ . GetMember (
116
+ prop ,
117
+ MemberTypes . Field | MemberTypes . Property ,
118
+ BindingFlags . Public | BindingFlags . NonPublic | BindingFlags . Instance | BindingFlags . FlattenHierarchy )
119
+ . FirstOrDefault ( ) ;
120
+
121
+ propExpr = propertyOrField ? . MemberType switch
122
+ {
123
+ MemberTypes . Property => Expression . Property ( expr , ( PropertyInfo ) propertyOrField ) ,
124
+ MemberTypes . Field => Expression . Field ( expr , ( FieldInfo ) propertyOrField ) ,
125
+ _ => null
126
+ } ;
127
+
128
+ return propExpr != null ;
48
129
}
49
130
50
131
private static bool IsReferenceAssignableFrom ( this Type destType , Type srcType )
@@ -136,14 +217,14 @@ public static Delegate Compile(this LambdaExpression exp, CompileArgument arg)
136
217
if ( source . Type . IsArray )
137
218
{
138
219
return source . Type . GetArrayRank ( ) == 1
139
- ? ( Expression ? ) Expression . ArrayLength ( source )
220
+ ? ( Expression ? ) Expression . ArrayLength ( source )
140
221
: Expression . Property ( source , "Length" ) ;
141
222
}
142
223
else
143
224
{
144
225
var countProperty = GetCount ( source . Type ) ;
145
- return countProperty != null
146
- ? Expression . Property ( source , countProperty )
226
+ return countProperty != null
227
+ ? Expression . Property ( source , countProperty )
147
228
: null ;
148
229
}
149
230
}
@@ -331,11 +412,11 @@ public static Expression ApplyNullPropagation(this Expression getter)
331
412
var result = getter ;
332
413
while ( current . NodeType == ExpressionType . MemberAccess )
333
414
{
334
- var memEx = ( MemberExpression ) current ;
415
+ var memEx = ( MemberExpression ) current ;
335
416
var expr = memEx . Expression ;
336
417
if ( expr == null )
337
418
break ;
338
- if ( expr . NodeType == ExpressionType . Parameter )
419
+ if ( expr . NodeType == ExpressionType . Parameter )
339
420
return result ;
340
421
341
422
if ( expr . CanBeNull ( ) )
0 commit comments