< Summary

Information
Class: MyNet.Observable.Collections.Filters.FilterGroup<T>
Assembly: MyNet.Observable
File(s): https://raw.githubusercontent.com/sandre58/MyNet/85372080fe102cd9ee155ceab49ae000e7f66103/src/MyNet.Observable/Collections/Filters/FilterGroup.cs
Tag: 323_28699572109
Line coverage
95%
Covered lines: 20
Uncovered lines: 1
Coverable lines: 21
Total lines: 92
Line coverage: 95.2%
Branch coverage
75%
Covered branches: 9
Total branches: 12
Branch coverage: 75%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
ProvideExpression()87.5%8893.33%
ReplaceParameter(...)50%22100%
.ctor(...)100%11100%
VisitParameter(...)50%22100%

File(s)

https://raw.githubusercontent.com/sandre58/MyNet/85372080fe102cd9ee155ceab49ae000e7f66103/src/MyNet.Observable/Collections/Filters/FilterGroup.cs

#LineLine coverage
 1// -----------------------------------------------------------------------
 2// <copyright file="FilterGroup.cs" company="Stéphane ANDRE">
 3// Copyright (c) Stéphane ANDRE. All rights reserved.
 4// </copyright>
 5// -----------------------------------------------------------------------
 6
 7using System;
 8using System.Collections.Generic;
 9using System.Linq;
 10using System.Linq.Expressions;
 11using MyNet.Primitives;
 12
 13namespace MyNet.Observable.Collections.Filters;
 14
 15/// <summary>
 16/// Represents a group of filter nodes combined with a logical operator (AND/OR).
 17/// </summary>
 18/// <param name="operator">The logical operator to combine the filter nodes.</param>
 19/// <param name="children">The child filter nodes to be combined.</param>
 20/// <typeparam name="T">The type of items to be filtered.</typeparam>
 21public sealed class FilterGroup<T>(LogicalOperator @operator, IEnumerable<IFilter<T>> children) : IFilter<T>
 22{
 23    /// <summary>
 24    /// Gets the logical operator used to combine the child filter nodes.
 25    /// </summary>
 1526    public LogicalOperator Operator { get; } = @operator;
 27
 28    /// <summary>
 29    /// Gets the child filter nodes that are combined using the specified logical operator.
 30    /// </summary>
 1531    public IReadOnlyList<IFilter<T>> Children { get; } = [.. children];
 32
 33    /// <summary>
 34    /// Converts the filter group into a single expression that can be used to evaluate whether an item of type T matche
 35    /// </summary>
 36    /// <returns>An expression representing the combined filter criteria.</returns>
 37    /// <exception cref="NotSupportedException">Thrown when an unsupported logical operator is encountered.</exception>
 38    public Expression<Func<T, bool>> ProvideExpression()
 39    {
 1540        if (Children.Count == 0)
 341            return _ => true;
 42
 1243        var param = Expression.Parameter(typeof(T), "x");
 44
 1245        var expressions = Children
 1246            .Select(c => ReplaceParameter(c.ProvideExpression(), param))
 1247            .ToList();
 48
 1249        var body = expressions[0];
 50
 4851        for (var i = 1; i < expressions.Count; i++)
 52        {
 1253            body = Operator switch
 1254            {
 655                LogicalOperator.And => Expression.AndAlso(body, expressions[i]),
 656                LogicalOperator.Or => Expression.OrElse(body, expressions[i]),
 057                _ => throw new NotSupportedException()
 1258            };
 59        }
 60
 1261        return Expression.Lambda<Func<T, bool>>(body, param);
 62    }
 63
 64    /// <summary>
 65    /// Replaces the parameter in the given expression with a new parameter. This is necessary to ensure that all expres
 66    /// </summary>
 67    /// <param name="expression">The expression in which to replace the parameter.</param>
 68    /// <param name="newParam">The new parameter to use in the expression.</param>
 69    /// <returns>The expression with the parameter replaced.</returns>
 70    /// <exception cref="InvalidOperationException">Thrown when the expression body is null.</exception>
 71    private static Expression ReplaceParameter(Expression<Func<T, bool>> expression, ParameterExpression newParam)
 72    {
 2473        var visitor = new ReplaceParameterVisitor(expression.Parameters[0], newParam);
 2474        return visitor.Visit(expression.Body) ?? throw new InvalidOperationException("Expression body cannot be null.");
 75    }
 76
 77    /// <summary>
 78    /// An expression visitor that replaces a specified parameter in an expression tree with a new parameter. This visit
 79    /// </summary>
 80    /// <param name="oldParam">The parameter to be replaced.</param>
 81    /// <param name="newParam">The parameter to replace with.</param>
 2482    private sealed class ReplaceParameterVisitor(ParameterExpression oldParam, ParameterExpression newParam) : Expressio
 83    {
 84        /// <summary>
 85        /// Visits the <see cref="ParameterExpression"/> nodes in the expression tree and replaces occurrences of the ol
 86        /// </summary>
 87        /// <param name="node">The parameter expression node to visit.</param>
 88        /// <returns>The modified expression node.</returns>
 2489        protected override Expression VisitParameter(ParameterExpression node) => node == oldParam ? newParam : base.Vis
 90    }
 91}
 92