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

NetFabric / NetFabric.CodeAnalysis / 6277926364

22 Sep 2023 06:38PM UTC coverage: 73.953% (-9.2%) from 83.158%
6277926364

push

github

web-flow
Add IsIndexable (#28)

323 of 452 branches covered (0.0%)

Branch coverage included in aggregate %.

349 of 349 new or added lines in 18 files covered. (100.0%)

648 of 861 relevant lines covered (75.26%)

20.93 hits per line

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

80.0
/NetFabric.Reflection/Expressions/ExpressionEx.ForEach.cs
1
using System;
2
using System.Collections;
3
using System.Linq.Expressions;
4
using System.Reflection;
5
using NetFabric.Reflection;
6
using static System.Linq.Expressions.Expression;
7

8
namespace NetFabric.Expressions;
9

10
public static partial class ExpressionEx
11
{
12
    /// <summary>
13
    /// Creates a <see cref="System.Linq.Expressions.Expression"/> with behaviour similar to the <c>foreach</c> statement in C#.
14
    /// </summary>
15
    /// <param name="enumerable">An <see cref="System.Linq.Expressions.Expression"/> that defines the enumerator.</param>
16
    /// <param name="body">
17
    /// A <see cref="System.Func&lt;Expression, Expression&gt;"/> that returns the body, given an
18
    /// <see cref="System.Linq.Expressions.Expression"/> that defines the item on each loop iteration.
19
    /// </param>
20
    /// <returns>The created <see cref="System.Linq.Expressions.Expression"/>.</returns>
21
    /// <exception cref="Exception">Object is not enumerable.</exception>
22
    /// <remarks>
23
    /// <p>
24
    /// The created <see cref="System.Linq.Expressions.Expression"/> depends on if the object defined in <paramref name="enumerable"/>
25
    /// is an <c>interface</c>, <c>class</c>, <c>struct</c>, <c>ref struct</c>, and if is disposable.
26
    /// </p>
27
    /// </remarks>
28
    public static Expression ForEach(Expression enumerable, Func<Expression, Expression> body)
29
    {
30
        var enumerableType = enumerable.Type;
56✔
31
        if (enumerableType.IsEnumerable(out var enumerableInfo, out var error))
56!
32
            return ForEach(enumerableInfo, enumerable, body);
56✔
33

34
        return error switch
×
35
        {
×
36
            IsEnumerableError.MissingGetEnumerator => throw new Exception($"'{enumerableType}' does not contain a public definition for 'GetEnumerator'"),
×
37
            IsEnumerableError.MissingCurrent => throw new Exception($"'{enumerableInfo!.GetEnumerator.ReturnType}' does not contain a public definition for 'Current'"),
×
38
            IsEnumerableError.MissingMoveNext => throw new Exception($"'{enumerableInfo!.GetEnumerator.ReturnType}' does not contain a public definition for 'MoveNext'"),
×
39
            _ => throw new Exception("Unhandled error!"),
×
40
        };
×
41
    }
42

43
    /// <summary>
44
    /// Creates a <see cref="System.Linq.Expressions.Expression"/> with behaviour similar to the <c>foreach</c> statement in C#.
45
    /// </summary>
46
    /// <param name="enumerableInfo">The information returned by a call to <see cref="NetFabric.Reflection.TypeExtensions.IsEnumerable(Type, out EnumerableInfo)"/>.</param>
47
    /// <param name="enumerable">An <see cref="System.Linq.Expressions.Expression"/> that defines the enumerator.</param>
48
    /// <param name="body">
49
    /// A <see cref="System.Func&lt;Expression, Expression&gt;"/> that returns the body, given an
50
    /// <see cref="System.Linq.Expressions.Expression"/> that defines the item on each loop iteration.
51
    /// </param>
52
    /// <returns>The created <see cref="System.Linq.Expressions.Expression"/>.</returns>
53
    /// <remarks>
54
    /// <p>
55
    /// The created <see cref="System.Linq.Expressions.Expression"/> depends on if the object defined in <paramref name="enumerable"/>
56
    /// is an <c>interface</c>, <c>class</c>, <c>struct</c>, <c>ref struct</c>, and if is disposable.
57
    /// </p>
58
    /// </remarks>
59
    public static Expression ForEach(EnumerableInfo enumerableInfo, Expression enumerable, Func<Expression, Expression> body)
60
    {
61
        if(enumerableInfo.ForEachUsesIndexer)
56✔
62
        {
63
            if (enumerable.Type.IsSpanOrReadOnlySpan())
8✔
64
            {
65
                // throws exception because the indexer returns a reference to the item and 
66
                // references are not yet supported by expression trees
67
                throw new NotSupportedException("ForEach Expression creation for Span<> or ReadOnlySpan<> is not supported.");
2✔
68
            }
69

70
            return UseIndexer(enumerable, body);
6✔
71
        }
72
        
73
        return UseEnumerable(enumerableInfo, enumerable, body);
48✔
74
        
75
        static Expression UseIndexer(Expression array, Func<Expression, Expression> body)
76
        {
77
            var arrayType = array.Type;
6✔
78
            return arrayType.IsIndexable(out var indexableInfo, out var error)
6!
79
                ? For(array, indexableInfo, body)
6✔
80
                : error switch
6✔
81
                {
6✔
82
                    IsIndexableError.MissingIndexer => throw new Exception($"'{arrayType}' does not contain a public definition for 'this'"),
×
83
                    IsIndexableError.MissingCountOrLength => throw new Exception($"'{arrayType}' does not contain a public definition for 'Count' or 'Length'"),
×
84
                    _ => throw new Exception("Unhandled error!"),
×
85
                };
6✔
86
        }
87

88
        static Expression UseEnumerable(EnumerableInfo enumerableInfo, Expression enumerable, Func<Expression, Expression> body)
89
        {
90
            var enumeratorType = enumerableInfo.GetEnumerator.ReturnType;
48✔
91
            var enumeratorInfo = enumerableInfo.EnumeratorInfo;
48✔
92
            var enumeratorVariable = Variable(enumeratorType, "enumerator");
48✔
93
            return Block(
48✔
94
                new[] { enumeratorVariable },
48✔
95
                Assign(enumeratorVariable, CallGetEnumerator(enumerableInfo, enumerable)),
48✔
96
                enumeratorInfo switch
48✔
97
                {
48✔
98
                    {Dispose: null} => NotDisposable(enumeratorInfo, enumeratorVariable, body),
24✔
99
                    _ => Disposable(enumeratorInfo, enumeratorVariable, body)
24✔
100
                });
48✔
101

102
            static Expression CallGetEnumerator(EnumerableInfo enumerableInfo, Expression enumerable)
103
                => enumerableInfo.EnumeratorInfo switch
48✔
104
                {
48✔
105
                    {IsGenericsEnumeratorInterface: true}
48✔
106
                        => Call(Convert(enumerable, enumerableInfo.GetEnumerator.DeclaringType!), enumerableInfo.GetEnumerator),
6✔
107
                    
48✔
108
                    {IsEnumeratorInterface: true}
48✔
109
                        => Call(Convert(enumerable, typeof(IEnumerable)), enumerableInfo.GetEnumerator),
6✔
110
                    
48✔
111
                    _ => Call(enumerable, enumerableInfo.GetEnumerator)
36✔
112
                };
48✔
113

114
            static Expression Disposable(EnumeratorInfo enumeratorInfo, ParameterExpression enumerator, Func<Expression, Expression> body)
115
                => Using(enumerator, 
24✔
116
                    EnumerationLoop(enumeratorInfo, enumerator, body)
24✔
117
                );
24✔
118

119
            static Expression NotDisposable(EnumeratorInfo enumeratorInfo, ParameterExpression enumerator, Func<Expression, Expression> body)
120
            {
121
                return enumeratorInfo switch
24✔
122
                {
24✔
123
                    {IsValueType: true} => NotDisposableValueType(enumeratorInfo, enumerator, body),
12✔
124
                    _ => NotDisposableReferenceType(enumeratorInfo, enumerator, body)
12✔
125
                };
24✔
126

127
                static Expression NotDisposableValueType(EnumeratorInfo enumeratorInfo, ParameterExpression enumerator, Func<Expression, Expression> body)
128
                    => EnumerationLoop(enumeratorInfo, enumerator, body);
12✔
129

130
                static Expression NotDisposableReferenceType(EnumeratorInfo enumeratorInfo, ParameterExpression enumerator, Func<Expression, Expression> body)
131
                {
132
                    var disposable = Variable(typeof(IDisposable), "disposable");
12✔
133
                    return TryFinally(
12✔
134
                        EnumerationLoop(enumeratorInfo, enumerator, body),
12✔
135
                        Block(
12✔
136
                            new[] {disposable},
12✔
137
                            Assign(disposable, TypeAs(enumerator, typeof(IDisposable))),
12✔
138
                            IfThen(
12✔
139
                                NotEqual(disposable, Constant(null)),
12✔
140
                                Call(disposable,
12✔
141
                                    typeof(IDisposable).GetPublicMethod(NameOf.Dispose)!)
12✔
142
                            )
12✔
143
                        )
12✔
144
                    );
12✔
145
                }
146
            }
147

148
            static Expression EnumerationLoop(EnumeratorInfo enumeratorInfo, ParameterExpression enumerator, Func<Expression, Expression> body)
149
                => While(Call(enumerator, enumeratorInfo.MoveNext),
48✔
150
                    body(Property(enumerator, enumeratorInfo.Current))
48✔
151
                );
48✔
152
        }
153
    }
154
}
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