1
- using System ;
1
+ using System ;
2
2
using System . Collections ;
3
3
using System . Linq ;
4
4
using System . Reflection ;
5
+ using System . Text . RegularExpressions ;
5
6
6
7
namespace WinUI . TableView . Extensions ;
7
8
8
9
/// <summary>
9
10
/// Provides extension methods for object types.
10
11
/// </summary>
11
- internal static class ObjectExtensions
12
+ internal static partial class ObjectExtensions
12
13
{
14
+ // Regex to split property paths into property names and indexers (for cases like e.g. "[2].Foo[0].Bar", where Foo might be a Property that returns an array)
15
+ [ GeneratedRegex ( @"([^.[]+)|(\[[^\]]+\])" , RegexOptions . Compiled ) ]
16
+ private static partial Regex PropertyPathRegex ( ) ;
17
+
13
18
/// <summary>
14
19
/// Gets the value of a property from an object using a sequence of property info and index pairs.
15
20
/// </summary>
16
21
/// <param name="obj">The object from which to get the value.</param>
17
- /// <param name="members ">An array of member info and index pairs.</param>
22
+ /// <param name="pis ">An array of property info and index pairs.</param>
18
23
/// <returns>The value of the property, or null if the object is null.</returns>
19
- internal static object ? GetValue ( this object ? obj , ( MemberInfo info , object ? index ) [ ] members )
24
+ internal static object ? GetValue ( this object ? obj , ( PropertyInfo pi , object ? index ) [ ] pis )
20
25
{
21
- foreach ( var ( info , index ) in members )
26
+ foreach ( var ( pi , index ) in pis )
22
27
{
23
28
if ( obj is null )
24
- {
25
29
break ;
26
- }
27
30
28
- if ( info is PropertyInfo pi )
31
+ if ( pi != null )
32
+ {
33
+ // Use property getter, with or without index
29
34
obj = index is not null ? pi . GetValue ( obj , [ index ] ) : pi . GetValue ( obj ) ;
30
- else if ( info is MethodInfo mi )
31
- obj = index is not null ? mi . Invoke ( obj , [ index ] ) : mi . Invoke ( obj , [ ] ) ;
35
+ }
36
+ else if ( index is int i )
37
+ {
38
+ // Array
39
+ if ( obj is Array arr )
40
+ {
41
+ obj = arr . GetValue ( i ) ;
42
+ }
43
+ // IList
44
+ else if ( obj is IList list )
45
+ {
46
+ obj = list [ i ] ;
47
+ }
48
+ else
49
+ {
50
+ // Not a supported indexer type
51
+ return null ;
52
+ }
53
+ }
54
+ else
55
+ {
56
+ // Not a supported path segment
57
+ return null ;
58
+ }
32
59
}
33
60
34
61
return obj ;
@@ -40,51 +67,92 @@ internal static class ObjectExtensions
40
67
/// <param name="obj">The object from which to get the value.</param>
41
68
/// <param name="type">The type of the object.</param>
42
69
/// <param name="path">The property path.</param>
43
- /// <param name="members ">An array of member info and index pairs.</param>
70
+ /// <param name="pis ">An array of property info and index pairs.</param>
44
71
/// <returns>The value of the property, or null if the object is null.</returns>
45
- internal static object ? GetValue ( this object ? obj , Type ? type , string ? path , out ( MemberInfo info , object ? index ) [ ] members )
72
+ internal static object ? GetValue ( this object ? obj , Type ? type , string ? path , out ( PropertyInfo pi , object ? index ) [ ] pis )
46
73
{
47
- var parts = path ? . Split ( '.' ) ;
74
+ if ( obj == null || string . IsNullOrWhiteSpace ( path ) || type == null )
75
+ {
76
+ pis = [ ] ;
77
+ return obj ;
78
+ }
48
79
49
- if ( parts is null )
80
+ var matches = PropertyPathRegex ( ) . Matches ( path ) ;
81
+ if ( matches . Count == 0 )
50
82
{
51
- members = [ ] ;
83
+ pis = [ ] ;
52
84
return obj ;
53
85
}
54
86
55
- members = new ( MemberInfo , object ? ) [ parts . Length ] ;
87
+ // Pre-size the steps array to the number of matches
88
+ pis = new ( PropertyInfo , object ? ) [ matches . Count ] ;
89
+ int i = 0 ;
90
+ object ? current = obj ;
91
+ Type ? currentType = type ;
56
92
57
- for ( var i = 0 ; i < parts . Length ; i ++ )
93
+ foreach ( Match match in matches )
58
94
{
59
- var part = parts [ i ] ;
60
- var index = default ( object ? ) ;
61
- if ( part . StartsWith ( '[' ) && part . EndsWith ( ']' ) )
62
- {
63
- index = int . TryParse ( part [ 1 ..^ 1 ] , out var ind ) ? ind : index ;
64
- part = "Item" ;
65
- }
95
+ string part = match . Value ;
96
+ object ? index = null ;
97
+ PropertyInfo ? pi = null ;
66
98
67
- if ( type ? . GetProperty ( part ) is { } pi )
68
- {
69
- members [ i ] = ( pi , index ) ;
70
- obj = index is not null ? pi ? . GetValue ( obj , [ index ] ) : pi ? . GetValue ( obj ) ;
71
- type = obj ? . GetType ( ) ;
72
- }
73
- else if ( type ? . IsArray is true && type . GetMethod ( "GetValue" , [ typeof ( int ) ] ) is { } mi )
99
+ if ( part . StartsWith ( '[' ) && part . EndsWith ( ']' ) )
74
100
{
75
- members [ i ] = ( mi , index ) ;
76
- obj = index is not null ? mi ? . Invoke ( obj , [ index ] ) : mi ? . Invoke ( obj , [ ] ) ;
77
- type = obj ? . GetType ( ) ;
78
-
101
+ // Indexer: [index] or [key]
102
+ string indexer = part [ 1 ..^ 1 ] ;
103
+ if ( int . TryParse ( indexer , out int intIndex ) )
104
+ index = intIndex ;
105
+ else
106
+ index = indexer ;
107
+
108
+ // Try array
109
+ if ( current is Array arr && index is int idx )
110
+ {
111
+ current = arr . GetValue ( idx ) ;
112
+ pis [ i ++ ] = ( null ! , idx ) ;
113
+ currentType = current ? . GetType ( ) ;
114
+ continue ;
115
+ }
116
+
117
+ // Try IList
118
+ if ( current is IList list && index is int idx2 )
119
+ {
120
+ current = list [ idx2 ] ;
121
+ pis [ i ++ ] = ( null ! , idx2 ) ;
122
+ currentType = current ? . GetType ( ) ;
123
+ continue ;
124
+ }
125
+
126
+ // Try default indexer property (e.g., this[string])
127
+ pi = currentType ? . GetProperty ( "Item" ) ;
128
+ if ( pi != null )
129
+ {
130
+ current = pi . GetValue ( current , [ index ] ) ;
131
+ pis [ i ++ ] = ( pi , index ) ;
132
+ currentType = current ? . GetType ( ) ;
133
+ continue ;
134
+ }
135
+
136
+ // Not found
137
+ pis = null ! ;
138
+ return null ;
79
139
}
80
140
else
81
141
{
82
- members = null ! ;
83
- return null ;
142
+ // Property access
143
+ pi = currentType ? . GetProperty ( part , BindingFlags . Instance | BindingFlags . Public | BindingFlags . NonPublic ) ;
144
+ if ( pi == null )
145
+ {
146
+ pis = null ! ;
147
+ return null ;
148
+ }
149
+ current = pi . GetValue ( current ) ;
150
+ pis [ i ++ ] = ( pi , null ) ;
151
+ currentType = current ? . GetType ( ) ;
84
152
}
85
153
}
86
154
87
- return obj ;
155
+ return current ;
88
156
}
89
157
90
158
/// <summary>
0 commit comments