• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

loresoft / EntityChange / 10792597827

10 Sep 2024 12:38PM UTC coverage: 39.608%. Remained the same
10792597827

push

github

web-flow
Merge pull request #103 from loresoft/dependabot/nuget/MinVer-6.0.0

Bump MinVer from 5.0.0 to 6.0.0

237 of 731 branches covered (32.42%)

Branch coverage included in aggregate %.

571 of 1309 relevant lines covered (43.62%)

60.49 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

8.93
/src/EntityChange/Reflection/LateBinder.cs
1
using System.Linq.Expressions;
2
using System.Reflection;
3

4
namespace EntityChange.Reflection;
5

6
/// <summary>
7
/// A class for late bound operations on a <see cref="Type"/>.
8
/// </summary>
9
public static class LateBinder
10
{
11
    /// <summary>
12
    /// The default public flags
13
    /// </summary>
14
    public const BindingFlags DefaultPublicFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy;
15
    /// <summary>
16
    /// The default non public flags
17
    /// </summary>
18
    public const BindingFlags DefaultNonPublicFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy;
19

20
    /// <summary>
21
    /// Searches for the specified method with the specified name.
22
    /// </summary>
23
    /// <param name="type">The <see cref="Type"/> to search for the method in.</param>
24
    /// <param name="name">The name of the method to find.</param>
25
    /// <param name="arguments">The arguments.</param>
26
    /// <returns>
27
    /// An <see cref="IMethodAccessor"/> instance for the method if found; otherwise <c>null</c>.
28
    /// </returns>
29
    public static IMethodAccessor FindMethod(Type type, string name, params object[] arguments)
30
    {
31
        return FindMethod(type, name, DefaultPublicFlags, arguments);
×
32
    }
33

34
    /// <summary>
35
    /// Searches for the specified method, using the specified binding constraints.
36
    /// </summary>
37
    /// <param name="type">The <see cref="Type"/> to search for the method in.</param>
38
    /// <param name="name">The name of the method to find.</param>
39
    /// <param name="flags">A bitmask comprised of one or more <see cref="BindingFlags"/> that specify how the search is conducted.</param>
40
    /// <param name="arguments">The arguments.</param>
41
    /// <returns>
42
    /// An <see cref="IMethodAccessor"/> instance for the method if found; otherwise <c>null</c>.
43
    /// </returns>
44
    public static IMethodAccessor FindMethod(Type type, string name, BindingFlags flags, params object[] arguments)
45
    {
46
        if (type == null)
×
47
            throw new ArgumentNullException(nameof(type));
×
48

49
        if (string.IsNullOrEmpty(name))
×
50
            throw new ArgumentNullException(nameof(name));
×
51

52
        var typeAccessor = TypeAccessor.GetAccessor(type);
×
53

54
        var types = arguments
×
55
          .Select(a => a?.GetType() ?? typeof(object))
×
56
          .ToArray();
×
57

58
        var methodAccessor = typeAccessor.FindMethod(name, types, flags);
×
59

60
        return methodAccessor;
×
61
    }
62

63
    /// <summary>
64
    /// Searches for the property using a property expression.
65
    /// </summary>
66
    /// <typeparam name="T">The object type containing the property specified in the expression.</typeparam>
67
    /// <param name="propertyExpression">The property expression (e.g. p => p.PropertyName)</param>
68
    /// <returns>An <see cref="IMemberAccessor"/> instance for the property if found; otherwise <c>null</c>.</returns>
69
    /// <exception cref="ArgumentNullException">Thrown if the <paramref name="propertyExpression"/> is null.</exception>
70
    /// <exception cref="ArgumentException">Thrown when the expression is:<br/>
71
    ///     Not a <see cref="MemberExpression"/><br/>
72
    ///     The <see cref="MemberExpression"/> does not represent a property.<br/>
73
    ///     Or, the property is static.
74
    /// </exception>
75
    public static IMemberAccessor FindProperty<T>(Expression<Func<T>> propertyExpression)
76
    {
77
        if (propertyExpression == null)
×
78
            throw new ArgumentNullException(nameof(propertyExpression));
×
79

80
        var typeAccessor = TypeAccessor.GetAccessor(typeof(T));
×
81
        return typeAccessor.FindProperty(propertyExpression);
×
82
    }
83

84
    /// <summary>
85
    /// Searches for the public property with the specified name.
86
    /// </summary>
87
    /// <param name="type">The <see cref="Type"/> to search for the property in.</param>
88
    /// <param name="name">The name of the property to find.</param>
89
    /// <returns>
90
    /// An <see cref="IMemberAccessor"/> instance for the property if found; otherwise <c>null</c>.
91
    /// </returns>
92
    public static IMemberAccessor FindProperty(Type type, string name)
93
    {
94
        return FindProperty(type, name, DefaultPublicFlags);
×
95
    }
96

97
    /// <summary>
98
    /// Searches for the specified property, using the specified binding constraints.
99
    /// </summary>
100
    /// <param name="type">The <see cref="Type"/> to search for the property in.</param>
101
    /// <param name="name">The name of the property to find.</param>
102
    /// <param name="flags">A bitmask comprised of one or more <see cref="BindingFlags"/> that specify how the search is conducted.</param>
103
    /// <returns>
104
    /// An <see cref="IMemberAccessor"/> instance for the property if found; otherwise <c>null</c>.
105
    /// </returns>
106
    public static IMemberAccessor FindProperty(Type type, string name, BindingFlags flags)
107
    {
108
        if (type == null)
×
109
            throw new ArgumentNullException(nameof(type));
×
110

111
        if (string.IsNullOrEmpty(name))
×
112
            throw new ArgumentNullException(nameof(name));
×
113

114
        Type currentType = type;
×
115
        IMemberAccessor memberAccessor = null;
×
116

117
        // support nested property
118
        var parts = name.Split('.');
×
119
        foreach (var part in parts)
×
120
        {
121
            if (memberAccessor != null)
×
122
                currentType = memberAccessor.MemberType;
×
123

124
            var typeAccessor = TypeAccessor.GetAccessor(currentType);
×
125
            memberAccessor = typeAccessor.FindProperty(part, flags);
×
126
        }
127

128
        return memberAccessor;
×
129
    }
130

131
    /// <summary>
132
    /// Searches for the field with the specified name.
133
    /// </summary>
134
    /// <param name="type">The <see cref="Type"/> to search for the field in.</param>
135
    /// <param name="name">The name of the field to find.</param>
136
    /// <returns>
137
    /// An <see cref="IMemberAccessor"/> instance for the field if found; otherwise <c>null</c>.
138
    /// </returns>
139
    public static IMemberAccessor FindField(Type type, string name)
140
    {
141
        return FindField(type, name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
×
142
    }
143

144
    /// <summary>
145
    /// Searches for the field, using the specified binding constraints.
146
    /// </summary>
147
    /// <param name="type">The <see cref="Type"/> to search for the field in.</param>
148
    /// <param name="name">The name of the field to find.</param>
149
    /// <param name="flags">A bitmask comprised of one or more <see cref="BindingFlags"/> that specify how the search is conducted.</param>
150
    /// <returns>
151
    /// An <see cref="IMemberAccessor"/> instance for the field if found; otherwise <c>null</c>.
152
    /// </returns>
153
    public static IMemberAccessor FindField(Type type, string name, BindingFlags flags)
154
    {
155
        if (type == null)
×
156
            throw new ArgumentNullException(nameof(type));
×
157

158
        if (string.IsNullOrEmpty(name))
×
159
            throw new ArgumentNullException(nameof(name));
×
160

161
        var typeAccessor = TypeAccessor.GetAccessor(type);
×
162
        var memberAccessor = typeAccessor.FindField(name, flags);
×
163

164
        return memberAccessor;
×
165
    }
166

167
    /// <summary>
168
    /// Searches for the property or field with the specified name.
169
    /// </summary>
170
    /// <param name="type">The <see cref="Type"/> to search for the property or field in.</param>
171
    /// <param name="name">The name of the property or field to find.</param>
172
    /// <returns>
173
    /// An <see cref="IMemberAccessor"/> instance for the property or field if found; otherwise <c>null</c>.
174
    /// </returns>
175
    public static IMemberAccessor Find(Type type, string name)
176
    {
177
        return Find(type, name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
×
178
    }
179

180
    /// <summary>
181
    /// Searches for the property or field, using the specified binding constraints.
182
    /// </summary>
183
    /// <param name="type">The <see cref="Type"/> to search for the property or field in.</param>
184
    /// <param name="name">The name of the property or field to find.</param>
185
    /// <param name="flags">A bitmask comprised of one or more <see cref="BindingFlags"/> that specify how the search is conducted.</param>
186
    /// <returns>
187
    /// An <see cref="IMemberAccessor"/> instance for the property or field if found; otherwise <c>null</c>.
188
    /// </returns>
189
    public static IMemberAccessor Find(Type type, string name, BindingFlags flags)
190
    {
191
        if (type == null)
×
192
            throw new ArgumentNullException(nameof(type));
×
193
        if (string.IsNullOrEmpty(name))
×
194
            throw new ArgumentNullException(nameof(name));
×
195

196
        var typeAccessor = TypeAccessor.GetAccessor(type);
×
197
        var memberAccessor = typeAccessor.Find(name, flags);
×
198

199
        return memberAccessor;
×
200
    }
201

202
    /// <summary>
203
    /// Sets the property value with the specified name.
204
    /// </summary>
205
    /// <param name="target">The object whose property value will be set.</param>
206
    /// <param name="name">The name of the property to set.</param>
207
    /// <param name="value">The new value to be set.</param>
208
    /// <remarks>This method supports nested property names. An exmample name would be 'Person.Address.ZipCode'.</remarks>
209
    public static void SetProperty(object target, string name, object value)
210
    {
211
        SetProperty(target, name, value, DefaultPublicFlags);
×
212
    }
×
213

214
    /// <summary>
215
    /// Sets the property value with the specified name.
216
    /// </summary>
217
    /// <param name="target">The object whose property value will be set.</param>
218
    /// <param name="name">The name of the property to set.</param>
219
    /// <param name="value">The new value to be set.</param>
220
    /// <remarks>This method supports nested property names. An exmample name would be 'Person.Address.ZipCode'.</remarks>
221
    /// <param name="flags">A bitmask comprised of one or more <see cref="BindingFlags"/> that specify how the search is conducted.</param>
222
    public static void SetProperty(object target, string name, object value, BindingFlags flags)
223
    {
224
        if (target == null)
×
225
            throw new ArgumentNullException(nameof(target));
×
226

227
        if (string.IsNullOrEmpty(name))
×
228
            throw new ArgumentNullException(nameof(name));
×
229

230
        Type rootType = target.GetType();
×
231
        Type currentType = rootType;
×
232
        object currentTarget = target;
×
233

234
        IMemberAccessor memberAccessor = null;
×
235

236
        // support nested property
237
        var parts = name.Split('.');
×
238
        foreach (var part in parts)
×
239
        {
240
            if (memberAccessor != null)
×
241
            {
242
                currentTarget = memberAccessor.GetValue(currentTarget);
×
243
                currentType = memberAccessor.MemberType;
×
244
            }
245

246
            var typeAccessor = TypeAccessor.GetAccessor(currentType);
×
247
            memberAccessor = typeAccessor.FindProperty(part, flags);
×
248
        }
249

250
        if (memberAccessor == null)
×
251
            throw new InvalidOperationException($"Could not find property '{name}' in type '{rootType.Name}'.");
×
252

253
        memberAccessor.SetValue(currentTarget, value);
×
254
    }
×
255

256
    /// <summary>
257
    /// Sets the field value with the specified name.
258
    /// </summary>
259
    /// <param name="target">The object whose field value will be set.</param>
260
    /// <param name="name">The name of the field to set.</param>
261
    /// <param name="value">The new value to be set.</param>
262
    public static void SetField(object target, string name, object value)
263
    {
264
        SetField(target, name, value, DefaultPublicFlags);
×
265
    }
×
266

267
    /// <summary>
268
    /// Sets the field value with the specified name.
269
    /// </summary>
270
    /// <param name="target">The object whose field value will be set.</param>
271
    /// <param name="name">The name of the field to set.</param>
272
    /// <param name="value">The new value to be set.</param>
273
    /// <param name="flags">A bitmask comprised of one or more <see cref="BindingFlags"/> that specify how the search is conducted.</param>
274
    public static void SetField(object target, string name, object value, BindingFlags flags)
275
    {
276
        if (target == null)
×
277
            throw new ArgumentNullException(nameof(target));
×
278

279
        if (string.IsNullOrEmpty(name))
×
280
            throw new ArgumentNullException(nameof(name));
×
281

282
        Type rootType = target.GetType();
×
283
        var memberAccessor = FindField(rootType, name, flags);
×
284

285
        if (memberAccessor == null)
×
286
            throw new InvalidOperationException($"Could not find field '{name}' in type '{rootType.Name}'.");
×
287

288
        memberAccessor.SetValue(target, value);
×
289
    }
×
290

291
    /// <summary>
292
    /// Sets the property or field value with the specified name.
293
    /// </summary>
294
    /// <param name="target">The object whose property or field value will be set.</param>
295
    /// <param name="name">The name of the property or field to set.</param>
296
    /// <param name="value">The new value to be set.</param>
297
    public static void Set(object target, string name, object value)
298
    {
299
        Set(target, name, value, DefaultPublicFlags);
×
300
    }
×
301

302
    /// <summary>
303
    /// Sets the property or field value with the specified name.
304
    /// </summary>
305
    /// <param name="target">The object whose property or field value will be set.</param>
306
    /// <param name="name">The name of the property or field to set.</param>
307
    /// <param name="value">The new value to be set.</param>
308
    /// <param name="flags">A bitmask comprised of one or more <see cref="BindingFlags"/> that specify how the search is conducted.</param>
309
    public static void Set(object target, string name, object value, BindingFlags flags)
310
    {
311
        if (target == null)
×
312
            throw new ArgumentNullException(nameof(target));
×
313

314
        if (string.IsNullOrEmpty(name))
×
315
            throw new ArgumentNullException(nameof(name));
×
316

317
        Type rootType = target.GetType();
×
318
        var memberAccessor = Find(rootType, name, flags);
×
319

320
        if (memberAccessor == null)
×
321
            throw new InvalidOperationException($"Could not find a property or field with a name of '{name}' in type '{rootType.Name}'.");
×
322

323
        memberAccessor.SetValue(target, value);
×
324
    }
×
325

326
    /// <summary>
327
    /// Returns the value of the property with the specified name.
328
    /// </summary>
329
    /// <param name="target">The object whose property value will be returned.</param>
330
    /// <param name="name">The name of the property to read.</param>
331
    /// <returns>The value of the property.</returns>
332
    public static object GetProperty(object target, string name)
333
    {
334
        return GetProperty(target, name, DefaultPublicFlags);
161✔
335
    }
336

337
    /// <summary>
338
    /// Returns the value of the property with the specified name.
339
    /// </summary>
340
    /// <param name="target">The object whose property value will be returned.</param>
341
    /// <param name="name">The name of the property to read.</param>
342
    /// <param name="flags">A bitmask comprised of one or more <see cref="BindingFlags"/> that specify how the search is conducted.</param>
343
    /// <returns>The value of the property.</returns>
344
    public static object GetProperty(object target, string name, BindingFlags flags)
345
    {
346
        if (target == null)
161!
347
            throw new ArgumentNullException(nameof(target));
×
348

349
        if (string.IsNullOrEmpty(name))
161!
350
            throw new ArgumentNullException(nameof(name));
×
351

352
        Type rootType = target.GetType();
161✔
353
        Type currentType = rootType;
161✔
354
        object currentTarget = target;
161✔
355

356
        IMemberAccessor memberAccessor = null;
161✔
357

358
        // support nested property
359
        var parts = name.Split('.');
161✔
360
        foreach (var part in parts)
644✔
361
        {
362
            if (memberAccessor != null)
161!
363
            {
364
                currentTarget = memberAccessor.GetValue(currentTarget);
×
365
                currentType = memberAccessor.MemberType;
×
366
            }
367

368
            var typeAccessor = TypeAccessor.GetAccessor(currentType);
161✔
369
            memberAccessor = typeAccessor.FindProperty(part, flags);
161✔
370
        }
371

372
        if (memberAccessor == null)
161!
373
            throw new InvalidOperationException($"Could not find property '{name}' in type '{rootType.Name}'.");
×
374

375
        return memberAccessor.GetValue(currentTarget);
161✔
376
    }
377

378
    /// <summary>
379
    /// Returns the value of the field with the specified name.
380
    /// </summary>
381
    /// <param name="target">The object whose field value will be returned.</param>
382
    /// <param name="name">The name of the field to read.</param>
383
    /// <returns>The value of the field.</returns>
384
    public static object GetField(object target, string name)
385
    {
386
        return GetField(target, name, DefaultPublicFlags);
×
387
    }
388

389
    /// <summary>
390
    /// Returns the value of the field with the specified name.
391
    /// </summary>
392
    /// <param name="target">The object whose field value will be returned.</param>
393
    /// <param name="name">The name of the field to read.</param>
394
    /// <param name="flags">A bitmask comprised of one or more <see cref="BindingFlags"/> that specify how the search is conducted.</param>
395
    /// <returns>The value of the field.</returns>
396
    public static object GetField(object target, string name, BindingFlags flags)
397
    {
398
        if (target == null)
×
399
            throw new ArgumentNullException(nameof(target));
×
400

401
        if (string.IsNullOrEmpty(name))
×
402
            throw new ArgumentNullException(nameof(name));
×
403

404
        var rootType = target.GetType();
×
405
        var memberAccessor = FindField(rootType, name, flags);
×
406
        if (memberAccessor == null)
×
407
            throw new InvalidOperationException($"Could not find field '{name}' in type '{rootType.Name}'.");
×
408

409
        return memberAccessor.GetValue(target);
×
410
    }
411

412
    /// <summary>
413
    /// Returns the value of the property or field with the specified name.
414
    /// </summary>
415
    /// <param name="target">The object whose property or field value will be returned.</param>
416
    /// <param name="name">The name of the property or field to read.</param>
417
    /// <returns>The value of the property or field.</returns>
418
    public static object Get(object target, string name)
419
    {
420
        return Get(target, name, DefaultPublicFlags);
×
421
    }
422

423
    /// <summary>
424
    /// Returns the value of the property or field with the specified name.
425
    /// </summary>
426
    /// <param name="target">The object whose property or field value will be returned.</param>
427
    /// <param name="name">The name of the property or field to read.</param>
428
    /// <param name="flags">A bitmask comprised of one or more <see cref="BindingFlags"/> that specify how the search is conducted.</param>
429
    /// <returns>The value of the property or field.</returns>
430
    public static object Get(object target, string name, BindingFlags flags)
431
    {
432
        if (target == null)
×
433
            throw new ArgumentNullException(nameof(target));
×
434

435
        if (string.IsNullOrEmpty(name))
×
436
            throw new ArgumentNullException(nameof(name));
×
437

438
        var rootType = target.GetType();
×
439
        var memberAccessor = Find(rootType, name, flags);
×
440
        if (memberAccessor == null)
×
441
            throw new InvalidOperationException($"Could not find a property or field with a name of '{name}' in type '{rootType.Name}'.");
×
442

443
        return memberAccessor.GetValue(target);
×
444
    }
445

446
    /// <summary>
447
    /// Creates an instance of the specified type.
448
    /// </summary>
449
    /// <param name="type">The type to create.</param>
450
    /// <returns>A new instance of the specified type.</returns>
451
    public static object CreateInstance(Type type)
452
    {
453
        if (type == null)
×
454
            throw new ArgumentNullException(nameof(type));
×
455

456
        var typeAccessor = TypeAccessor.GetAccessor(type);
×
457
        if (typeAccessor == null)
×
458
            throw new InvalidOperationException($"Could not find constructor for {type.Name}.");
×
459

460
        return typeAccessor.Create();
×
461
    }
462

463
    /// <summary>
464
    /// Invokes the method with the spcified name and arguments.
465
    /// </summary>
466
    /// <param name="target">The target instance to call the method on.</param>
467
    /// <param name="name">The name of the method.</param>
468
    /// <param name="arguments">The method argument values.</param>
469
    /// <returns>The returned results from the method call.</returns>
470
    public static object InvokeMethod(object target, string name, params object[] arguments)
471
    {
472
        if (target == null)
×
473
            throw new ArgumentNullException(nameof(target));
×
474

475
        if (string.IsNullOrEmpty(name))
×
476
            throw new ArgumentNullException(nameof(name));
×
477

478
        var rootType = target.GetType();
×
479
        var methodAccessor = FindMethod(rootType, name);
×
480

481
        if (methodAccessor == null)
×
482
            throw new InvalidOperationException($"Could not find method '{name}' in type '{rootType.Name}'.");
×
483

484
        return methodAccessor.Invoke(target, arguments);
×
485

486
    }
487
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc