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