< Summary

Information
Class: MyNet.Reflection.PropertyQuery
Assembly: MyNet.Reflection
File(s): https://raw.githubusercontent.com/sandre58/MyNet/85372080fe102cd9ee155ceab49ae000e7f66103/src/MyNet.Reflection/PropertyCache.cs
Tag: 323_28699572109
Line coverage
0%
Covered lines: 0
Uncovered lines: 39
Coverable lines: 39
Total lines: 201
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 2
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
Where(...)100%210%
WhereAttribute(...)100%210%
ExcludeAttribute(...)100%210%
ExcludeNames(...)100%210%
Readable()100%210%
Writable()100%210%
WithoutIndexers()100%210%
ToArray()100%210%
ToNames()100%210%
Execute()0%620%
BuildCacheKey(...)100%210%

File(s)

https://raw.githubusercontent.com/sandre58/MyNet/85372080fe102cd9ee155ceab49ae000e7f66103/src/MyNet.Reflection/PropertyCache.cs

#LineLine coverage
 1// -----------------------------------------------------------------------
 2// <copyright file="PropertyCache.cs" company="Stéphane ANDRE">
 3// Copyright (c) Stéphane ANDRE. All rights reserved.
 4// </copyright>
 5// -----------------------------------------------------------------------
 6
 7using System;
 8using System.Collections.Concurrent;
 9using System.Collections.Generic;
 10using System.Linq;
 11using System.Reflection;
 12
 13namespace MyNet.Reflection;
 14
 15/// <summary>
 16/// Provides cached reflection queries for properties.
 17/// </summary>
 18public static class PropertyCache
 19{
 20    private static readonly ConcurrentDictionary<(Type Type, string Key), object> Cache = new();
 21
 22    /// <summary>
 23    /// Creates a property query for the specified type.
 24    /// </summary>
 25    /// <param name="type">The target type.</param>
 26    /// <returns>A new <see cref="PropertyQuery"/> instance.</returns>
 27    public static PropertyQuery For(Type type)
 28    {
 29        ArgumentNullException.ThrowIfNull(type);
 30
 31        return new(type);
 32    }
 33
 34    /// <summary>
 35    /// Creates a property query for the specified generic type parameter. This method is a convenient overload of the F
 36    /// </summary>
 37    /// <typeparam name="T">The target type.</typeparam>
 38    /// <returns>A new <see cref="PropertyQuery"/> instance.</returns>
 39    public static PropertyQuery For<T>() => For(typeof(T));
 40
 41    internal static TValue GetOrCreate<TValue>(
 42        Type type,
 43        string key,
 44        Func<TValue> factory)
 45        where TValue : class
 46    {
 47        ArgumentNullException.ThrowIfNull(type);
 48        ArgumentNullException.ThrowIfNull(key);
 49        ArgumentNullException.ThrowIfNull(factory);
 50
 51        return (TValue)Cache.GetOrAdd(
 52            (type, key),
 53            static (_, state) => state(),
 54            factory);
 55    }
 56}
 57
 58/// <summary>
 59/// Represents a cached property query.
 60/// </summary>
 61public sealed class PropertyQuery
 62{
 63    private readonly Type _type;
 064    private readonly List<Func<PropertyInfo, bool>> _predicates = [];
 65
 066    internal PropertyQuery(Type type) => _type = type;
 67
 68    /// <summary>
 69    /// Adds a custom filter predicate.
 70    /// </summary>
 71    /// <param name="predicate">The predicate to add.</param>
 72    /// <returns>The current query.</returns>
 73    public PropertyQuery Where(Func<PropertyInfo, bool> predicate)
 74    {
 075        ArgumentNullException.ThrowIfNull(predicate);
 76
 077        _predicates.Add(predicate);
 78
 079        return this;
 80    }
 81
 82    /// <summary>
 83    /// Includes only properties marked with the specified attribute.
 84    /// </summary>
 85    /// <typeparam name="TAttribute">The attribute type.</typeparam>
 86    /// <param name="inherit">
 87    /// Indicates whether inherited attributes should be considered.
 88    /// </param>
 89    /// <returns>The current query.</returns>
 90    public PropertyQuery WhereAttribute<TAttribute>(bool inherit = true)
 91        where TAttribute : Attribute
 092        => Where(property => property.IsDefined(typeof(TAttribute), inherit));
 93
 94    /// <summary>
 95    /// Excludes properties marked with the specified attribute.
 96    /// </summary>
 97    /// <typeparam name="TAttribute">The attribute type.</typeparam>
 98    /// <param name="inherit">
 99    /// Indicates whether inherited attributes should be considered.
 100    /// </param>
 101    /// <returns>The current query.</returns>
 102    public PropertyQuery ExcludeAttribute<TAttribute>(bool inherit = true)
 103        where TAttribute : Attribute
 0104        => Where(property => !property.IsDefined(typeof(TAttribute), inherit));
 105
 106    /// <summary>
 107    /// Excludes the specified property names.
 108    /// </summary>
 109    /// <param name="propertyNames">The property names to exclude.</param>
 110    /// <returns>The current query.</returns>
 111    public PropertyQuery ExcludeNames(params IEnumerable<string> propertyNames)
 112    {
 0113        ArgumentNullException.ThrowIfNull(propertyNames);
 114
 0115        var names = new HashSet<string>(propertyNames, StringComparer.Ordinal);
 116
 0117        return Where(property => !names.Contains(property.Name));
 118    }
 119
 120    /// <summary>
 121    /// Includes only readable properties.
 122    /// </summary>
 123    /// <returns>The current query.</returns>
 0124    public PropertyQuery Readable() => Where(static property => property.CanRead);
 125
 126    /// <summary>
 127    /// Includes only writable properties.
 128    /// </summary>
 129    /// <returns>The current query.</returns>
 0130    public PropertyQuery Writable() => Where(static property => property.CanWrite);
 131
 132    /// <summary>
 133    /// Excludes indexer properties.
 134    /// </summary>
 135    /// <returns>The current query.</returns>
 0136    public PropertyQuery WithoutIndexers() => Where(static property => property.GetIndexParameters().Length == 0);
 137
 138    /// <summary>
 139    /// Returns the matching properties.
 140    /// </summary>
 141    /// <returns>The matching properties.</returns>
 142    public PropertyInfo[] ToArray()
 143    {
 0144        var cacheKey = BuildCacheKey("properties");
 145
 0146        return PropertyCache.GetOrCreate(_type, cacheKey, Execute);
 147    }
 148
 149    /// <summary>
 150    /// Returns the matching property names.
 151    /// </summary>
 152    /// <returns>The matching property names.</returns>
 153    public string[] ToNames()
 154    {
 0155        var cacheKey = BuildCacheKey("names");
 156
 0157        return PropertyCache.GetOrCreate(
 0158            _type,
 0159            cacheKey,
 0160            () =>
 0161            {
 0162                var properties = Execute();
 0163
 0164                var result = new string[properties.Length];
 0165
 0166                for (var i = 0; i < properties.Length; i++)
 0167                {
 0168                    result[i] = properties[i].Name;
 0169                }
 0170
 0171                return result;
 0172            });
 173    }
 174
 175    /// <summary>
 176    /// Executes the property query by retrieving the public properties of the target type and applying all the specifie
 177    /// </summary>
 178    /// <returns>The filtered array of <see cref="PropertyInfo"/> objects.</returns>
 179    private PropertyInfo[] Execute()
 180    {
 0181        var properties = _type.GetPublicProperties();
 182
 0183        if (_predicates.Count == 0)
 184        {
 0185            return properties;
 186        }
 187
 0188        var result = new List<PropertyInfo>(properties.Length);
 0189        result.AddRange(from property in properties let include = _predicates.All(predicate => predicate(property)) wher
 190
 0191        return [.. result];
 192    }
 193
 194    /// <summary>
 195    /// Builds a cache key based on the specified suffix and the method names of the predicates. The cache key is constr
 196    /// </summary>
 197    /// <param name="suffix">The suffix to use for the cache key.</param>
 198    /// <returns>The constructed cache key.</returns>
 0199    private string BuildCacheKey(string suffix) => $"{suffix}:{string.Join("|", _predicates.Select(static x => x.Method.
 200}
 201