﻿<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0">
	<channel>
		<title>.net, C# and programming (EN)</title>
		<link>https://blog.rogatnev.net/</link>
		<description>.net, C# and programming</description>
		<copyright>Copyright © 2026</copyright>
		<managingEditor>Sergey Rogatnev</managingEditor>
		<pubDate>Sun, 26 Apr 2026 15:23:08 GMT</pubDate>
		<lastBuildDate>Sun, 26 Apr 2026 15:23:08 GMT</lastBuildDate>
		<item>
			<title>Collections search</title>
			<link>https://blog.rogatnev.net/posts/en/2026/04/Collections-search.html</link>
			<description>When O(N) is faster than O(1).</description>
			<author>Sergey Rogatnev</author>
			<guid>https://blog.rogatnev.net/posts/en/2026/04/Collections-search.html</guid>
			<pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;During interviews, we often hear — or say ourselves — that searching in an array is slower than in a hashtable.
Some might even recall that array search has linear complexity, or O(n), while a hash table has constant complexity, O(1).
But does this hold true in practice? What if there are situations where searching in an array turns out to be faster?
Let's not rush to conclusions.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;So, let's compare searching in an &lt;code&gt;array&lt;/code&gt; and a &lt;code&gt;HashSet&lt;/code&gt; on small collections, using different types: a primitive &lt;code&gt;int&lt;/code&gt; and a reference type &lt;code&gt;string&lt;/code&gt;.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Full benchmark code. Click to expand.&lt;/summary&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;using System.Collections.Generic;
using System.Linq;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;

namespace Benchmarks
{
[ShortRunJob(RuntimeMoniker.Net60)]
[ShortRunJob(RuntimeMoniker.Net80)]
[ShortRunJob(RuntimeMoniker.Net10_0)]
[HideColumns("Job", "Error", "StdDev", "Gen0", "Median")]
[DisassemblyDiagnoser(printSource: true, maxDepth: 2)]
public class HashSetContainsBenchmark
{
[Params(1, 10, 100, 1000)]
public int Size { get; set; }

        private int[] _arrayInt;
        private HashSet&amp;lt;int&amp;gt; _hashSetInt;
        private int _missingInt;

        private string[] _arrayString;
        private HashSet&amp;lt;string&amp;gt; _hashSetString;
        private string _missingString;

        [GlobalSetup]
        public void Setup()
        {
            // --- Int Setup ---
            var intData = Enumerable.Range(0, Size).ToArray();
            _arrayInt = intData;
            _hashSetInt = new HashSet&amp;lt;int&amp;gt;(intData);
            _missingInt = Size + 1;

            // --- String Setup ---
            var stringData = Enumerable.Range(0, Size).Select(i =&amp;gt; $"value-{i}").ToArray();
            _arrayString = stringData;
            _hashSetString = new HashSet&amp;lt;string&amp;gt;(stringData);
            _missingString = "value-a";
        }

        // --- Int Benchmarks ---
        [Benchmark(Description = "Array.Contains (Int)")]
        public bool Int_Array() =&amp;gt; _arrayInt.Contains(_missingInt);

        [Benchmark(Description = "HashSet.Contains (Int)")]
        public bool Int_HashSet() =&amp;gt; _hashSetInt.Contains(_missingInt);

        // --- String Benchmarks ---
        [Benchmark(Description = "Array.Contains (String)")]
        public bool String_Array() =&amp;gt; _arrayString.Contains(_missingString);

        [Benchmark(Description = "HashSet.Contains (String)")]
        public bool String_HashSet() =&amp;gt; _hashSetString.Contains(_missingString);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;pre&gt;&lt;code class="language-markdown"&gt;| Method                      | Runtime   | Size |     Mean | Code Size |
|-----------------------------|-----------|------|---------:|----------:|
|  Array.Contains (Int)       | .NET 6.0  | 1    | 3.443 ns |      50 B |
|  HashSet.Contains (Int)     | .NET 6.0  | 1    | 2.052 ns |     447 B |
|  Array.Contains (String)    | .NET 6.0  | 1    | 8.621 ns |      57 B |
|  HashSet.Contains (String)  | .NET 6.0  | 1    | 4.741 ns |     737 B |
|  Array.Contains (Int)       | .NET 8.0  | 1    | 2.676 ns |      40 B |
|  HashSet.Contains (Int)     | .NET 8.0  | 1    | 1.954 ns |     385 B |
|  Array.Contains (String)    | .NET 8.0  | 1    | 9.639 ns |      51 B |
|  HashSet.Contains (String)  | .NET 8.0  | 1    | 4.599 ns |     351 B |
|  Array.Contains (Int)       | .NET 10.0 | 1    | 1.193 ns |     669 B |
|  HashSet.Contains (Int)     | .NET 10.0 | 1    | 1.666 ns |     377 B |
|  Array.Contains (String)    | .NET 10.0 | 1    | 2.285 ns |     548 B |
|  HashSet.Contains (String)  | .NET 10.0 | 1    | 3.698 ns |     233 B |
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Intuitively, it seems that iterating over one element in an array should be faster than the more complex mechanism of hash-based searching.
As we can see, even when searching in a collection of a single int, the array loses to HashSet up until .NET 10:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/collections-search/one_element_int.png" alt="Search time in ns, size = 1"&gt;&lt;/p&gt;
&lt;p&gt;And even then, the advantage is quite small.&lt;/p&gt;
&lt;p&gt;We observe the same picture for strings:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/collections-search/one_element_string.png" alt="Search time in ns, size = 1"&gt;&lt;/p&gt;
&lt;p&gt;Notice how in .NET 10, array search for strings (2.285 ns) even beats HashSet (3.698 ns) — we'll explain why later.&lt;/p&gt;
&lt;p&gt;Alright, collections of one element are weird. Let's take the larger size — 10 elements. Surely, the advantage of HashSet should become more obvious there:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-markdown"&gt;| Method                      | Runtime   | Size |      Mean | Code Size |
|-----------------------------|-----------|------|----------:|----------:|
|  Array.Contains (Int)       | .NET 6.0  | 10   |  4.059 ns |      50 B |
|  HashSet.Contains (Int)     | .NET 6.0  | 10   |  2.901 ns |     447 B |
|  Array.Contains (String)    | .NET 6.0  | 10   | 33.465 ns |      57 B |
|  HashSet.Contains (String)  | .NET 6.0  | 10   |  5.807 ns |     737 B |
|  Array.Contains (Int)       | .NET 8.0  | 10   |  3.137 ns |      40 B |
|  HashSet.Contains (Int)     | .NET 8.0  | 10   |  2.497 ns |     397 B |
|  Array.Contains (String)    | .NET 8.0  | 10   | 31.164 ns |      51 B |
|  HashSet.Contains (String)  | .NET 8.0  | 10   |  5.937 ns |     377 B |
|  Array.Contains (Int)       | .NET 10.0 | 10   |  1.639 ns |     729 B |
|  HashSet.Contains (Int)     | .NET 10.0 | 10   |  2.230 ns |     377 B |
|  Array.Contains (String)    | .NET 10.0 | 10   | 18.063 ns |     548 B |
|  HashSet.Contains (String)  | .NET 10.0 | 10   |  4.583 ns |     233 B |
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Do you see the oddities already? Here they are:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/collections-search/ten_elements_int.png" alt="Search time in ns, size = 10"&gt;&lt;/p&gt;
&lt;p&gt;The search time in HashSet remains roughly the same regardless of the framework version, but array search keeps getting better and better.
Moreover, in .NET 10, the array overtakes &lt;code&gt;HashSet&lt;/code&gt;! How come?
Does array search also work in constant time now?
It seems we are on the verge of a grand discovery. Let's test our hypothesis on collections of 100 elements:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-markdown"&gt;| Method                      | Runtime   | Size |       Mean | Code Size |
|-----------------------------|-----------|------|-----------:|----------:|
|  Array.Contains (Int)       | .NET 6.0  | 100  |  13.073 ns |      50 B |
|  HashSet.Contains (Int)     | .NET 6.0  | 100  |   2.116 ns |     447 B |
|  Array.Contains (String)    | .NET 6.0  | 100  | 168.924 ns |      57 B |
|  HashSet.Contains (String)  | .NET 6.0  | 100  |   8.306 ns |     737 B |
|  Array.Contains (Int)       | .NET 8.0  | 100  |   4.381 ns |      40 B |
|  HashSet.Contains (Int)     | .NET 8.0  | 100  |   1.857 ns |     385 B |
|  Array.Contains (String)    | .NET 8.0  | 100  |  93.214 ns |      51 B |
|  HashSet.Contains (String)  | .NET 8.0  | 100  |   7.938 ns |     377 B |
|  Array.Contains (Int)       | .NET 10.0 | 100  |   3.401 ns |     736 B |
|  HashSet.Contains (Int)     | .NET 10.0 | 100  |   1.774 ns |     377 B |
|  Array.Contains (String)    | .NET 10.0 | 100  |  71.121 ns |     562 B |
|  HashSet.Contains (String)  | .NET 10.0 | 100  |   6.806 ns |     233 B |
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unfortunately, a miracle didn't happen. Now the &lt;code&gt;array&lt;/code&gt; consistently loses to &lt;code&gt;HashSet&lt;/code&gt; in searching.
But the difference for the int type is still not that significant:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/collections-search/compare_ints.png" alt="Search time in ns, size = 100"&gt;&lt;/p&gt;
&lt;p&gt;Let's look at the growth of search time in an array relative to its size.
For clarity, let's add an array of 1000 elements:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/collections-search/compare_ints_2.png" alt="Search time in ns, size = 1000"&gt;&lt;/p&gt;
&lt;p&gt;As we can see, only in .NET 6 do we begin to observe linear growth after 100 elements.
And in versions 8 and 10, the growth, if not logarithmic, is heavily smoothed out.&lt;/p&gt;
&lt;p&gt;It's time to dive deeper, specifically into exactly how array search is performed.
Calling the Contains method on an array ultimately leads to calling the static Array.IndexOf method.
But this is where the differences begin.&lt;/p&gt;
&lt;h1 id="net-6"&gt;.NET 6&lt;/h1&gt;
&lt;p&gt;Using some loop-unrolling optimizations and fast access to array elements, .NET checks each element for equality:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;...
while (length &amp;gt;= 8)
{
    length -= 8;

    if (value.Equals(Unsafe.Add(ref searchSpace, index)))
        goto Found;
    if (value.Equals(Unsafe.Add(ref searchSpace, index + 1)))
        goto Found1;
    if (value.Equals(Unsafe.Add(ref searchSpace, index + 2)))
        goto Found2;
    if (value.Equals(Unsafe.Add(ref searchSpace, index + 3)))
        goto Found3;
    if (value.Equals(Unsafe.Add(ref searchSpace, index + 4)))
        goto Found4;
    if (value.Equals(Unsafe.Add(ref searchSpace, index + 5)))
        goto Found5;
    if (value.Equals(Unsafe.Add(ref searchSpace, index + 6)))
        goto Found6;
    if (value.Equals(Unsafe.Add(ref searchSpace, index + 7)))
        goto Found7;

    index += 8;
}
// the remainder of the array (less than 8 elements) is checked next
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, we sequentially check each element and get our honest &lt;code&gt;O(N)&lt;/code&gt; complexity. Nothing unusual so far.&lt;/p&gt;
&lt;h1 id="net-8"&gt;.NET 8&lt;/h1&gt;
&lt;p&gt;Starting with this version, the situation changes drastically.
The &lt;code&gt;SpanHelpers.NonPackedIndexOfValueType&lt;/code&gt; method comes into play, which, in addition to loop unrolling, contains a powerful hardware optimization—vectorization:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;if (Vector512.IsHardwareAccelerated &amp;amp;&amp;amp; length &amp;gt;= Vector512&amp;lt;TValue&amp;gt;.Count)
{
    Vector512&amp;lt;TValue&amp;gt; left = Vector512.Create&amp;lt;TValue&amp;gt;(value);
    //...
}
else if (Vector256.IsHardwareAccelerated &amp;amp;&amp;amp; length &amp;gt;= Vector256&amp;lt;TValue&amp;gt;.Count)
{
    Vector256&amp;lt;TValue&amp;gt; left = Vector256.Create&amp;lt;TValue&amp;gt;(value);
    //...
}
else
{
    Vector128&amp;lt;TValue&amp;gt; left = Vector128.Create&amp;lt;TValue&amp;gt;(value);
    //...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can see the use of new types: &lt;code&gt;Vector128&lt;/code&gt;, &lt;code&gt;Vector256&lt;/code&gt;, &lt;code&gt;Vector512&lt;/code&gt; — collections for working with vector data.
And pay attention to the &lt;code&gt;IsHardwareAccelerated&lt;/code&gt; property being checked in every condition — this is where the magic lies.
It determines whether your processor supports working with vectors of 128, 256, and 512 bits in length, respectively.&lt;/p&gt;
&lt;p&gt;Using the &lt;code&gt;Vector512&lt;/code&gt; class allows operating on 16 int array elements at once.
512 bits = 64 bytes, meaning the vector holds 16 values of type int at 4 bytes each.&lt;/p&gt;
&lt;p&gt;Unfortunately, we now have to dive into assembler to better understand how this works:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-asm"&gt;cmp r8d,4
jl near ptr M01_L03          ; If elements &amp;lt; 4, process one by one
cmp r8d,10h                  ; 0x10 = 16 decimal
jl near ptr M01_L18          ; If elements &amp;lt; 16, switch to processing using a 256-bit register (ymm)
vpbroadcastd zmm0,edx        ; Load the target value edx into the 512-bit register zmm0. Now zmm0 looks like [edx, edx, edx, ..., edx]
...
vmovups zmm1,[rcx]           ; Load the first 16 elements from memory into the 512-bit register zmm1
vpcmpeqd k1,zmm1,zmm0        ; Compare zmm1 and zmm0 for equality
kortestw k1,k1               ; Check if there is at least one match
...
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;vpcmpeqd&lt;/code&gt; is an instruction that compares two vectors element-by-element and saves the result into a special register &lt;code&gt;k1&lt;/code&gt;. If the fifth element matches, the fifth bit in &lt;code&gt;k1&lt;/code&gt; will be set to 1.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;kortestw&lt;/code&gt; checks this bitmask. If it equals 0, it means the target number is not present in the first 16 elements.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At first glance, it looks complicated, but essentially, this is a continuation of the loop unrolling concept, just at the hardware level.
And the new framework versions hide all the complexity of such optimizations from us.
Ultimately, there is no magic, and array search is still &lt;code&gt;O(N)&lt;/code&gt;. Even if it is &lt;code&gt;O(N/16)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This magic only works on modern processors that support &lt;code&gt;AVX-512&lt;/code&gt; instructions (or &lt;code&gt;AVX2&lt;/code&gt; for 256-bit).
If you run this code on older hardware, .NET will gracefully fall back to &lt;code&gt;Vector128&lt;/code&gt; or a regular loop.&lt;/p&gt;
&lt;h1 id="net-10"&gt;.NET 10&lt;/h1&gt;
&lt;p&gt;Let me remind you that in this version, the results for an array of length 10 got even better and even surpassed &lt;code&gt;HashSet&lt;/code&gt;.
At the same time, the search algorithm itself hasn't changed; the code is exactly the same.
Registers larger than 512 bits haven't been invented yet. So, what's the deal? I assume that &lt;a href="https://en.wikipedia.org/wiki/Profile-guided_optimization"&gt;PGO&lt;/a&gt; (Profile-Guided Optimization) kicked in, and the compiler decided that searches will most often occur in medium-sized arrays (8–15 elements), rearranged the comparison blocks to avoid unnecessary jumps, and saved the processor a couple more clock cycles.&lt;/p&gt;
&lt;p&gt;And remember how earlier we made a mental note of why string array search became so good in .NET 10? It's time to figure that out as well.&lt;/p&gt;
&lt;p&gt;Let's look at the benchmark once again:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-markdown"&gt;| Method                  | Runtime   | Size |     Mean | Code Size |
|-------------------------|-----------|------|---------:|----------:|
| Array.Contains (String) | .NET 8.0  | 1    | 9.639 ns |      51 B |
| Array.Contains (String) | .NET 10.0 | 1    | 2.285 ns |     548 B |
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Besides the nearly fourfold difference in speed, we also see a multiple-fold difference in Code Size.
For .NET 8, the compiler didn't think twice and redirected the &lt;code&gt;Contains&lt;/code&gt; method call to the generic &lt;code&gt;Array.IndexOf&lt;/code&gt; method, which calls the virtual &lt;code&gt;bool Equals(object obj)&lt;/code&gt; method for each array element.
It's reliable, but not fast.&lt;/p&gt;
&lt;p&gt;However, in .NET 10, the compiler applied a new technique called "devirtualization" — more specifically, &lt;strong&gt;Guarded Devirtualization&lt;/strong&gt;.
It is understood that our collection is an array of strings and only strings.
This means we can skip all those generic calls and bypass the virtual method table dispatch, and simply inline the entire string comparison loop.
That is why we get 548 bytes of code versus 51. But! We get specific code for searching a string in an array, which works much faster. To see this, let's look at the assembly code again:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-asm"&gt;...
mov r8,offset MT_System.String
cmp [rcx],r8                ; Verify that we definitely have a string. Hence Guarded Devirtualization.
jne short M00_L08           ; Otherwise, compare via classic Equals
...
mov r8d,[rcx+8]             ; Read the length of the current string
cmp r8d,[rsi+8]             ; Compare it with the length of the target string
je short M00_L07            ; If lengths match - compare the strings themselves byte by byte
...
M00_L07:
lea rax,[rcx+0C]
...
lea rdx,[rsi+0C]            ; Get a pointer to the byte array where strings are stored
mov rcx,rax
call qword ptr [7FFD2A46C330] ; System.SpanHelpers.SequenceEqual - call the method that compares 2 byte arrays
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At the same time, &lt;code&gt;SequenceEqual&lt;/code&gt; also supports vector operations and hardware acceleration.
Wow! That's powerful! I didn't even suspect what a tremendous amount of work goes on behind a simple string search in an array.&lt;/p&gt;
&lt;h1 id="conclusions"&gt;Conclusions&lt;/h1&gt;
&lt;p&gt;Once again, the obvious conclusion suggests itself: simply updating .NET will yield a performance boost (and therefore resource savings).
Also, there is no magic; &lt;code&gt;O(N)&lt;/code&gt; is still &lt;code&gt;O(N)&lt;/code&gt;, which is clearly noticeable at large values. However, for small values (5–10 elements), things might not be so straightforward.&lt;/p&gt;
&lt;p&gt;But can we do better? To avoid O(N), even with hardware acceleration, but also without constantly paying the hashing overhead? We'll see...&lt;/p&gt;
&lt;h1 id="analyzer"&gt;Analyzer&lt;/h1&gt;
&lt;p&gt;I've added a &lt;a href="https://github.com/Backs/Collections.Analyzer/blob/master/Documentation/CI0008.md"&gt;rule&lt;/a&gt; to my &lt;a href="https://github.com/Backs/Collections.Analyzer"&gt;analyzer&lt;/a&gt; that will suggest replacing an array (or list) with a HashSet if Contains is called on it.
This is useful for .NET 8 and below, and remains relevant for .NET 10 with collections larger than ~15 elements. For finer tuning, you can adjust the trigger threshold in &lt;code&gt;.editorconfig&lt;/code&gt; — for example, only starting from five elements:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dotnet_diagnostic.CI0008.min_items_count = 5
&lt;/code&gt;&lt;/pre&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>Collection initializer: List&lt;T&gt;</title>
			<link>https://blog.rogatnev.net/posts/en/2025/11/List-initializer.html</link>
			<description>Evaluate the impact of list size pre-initialization on performance and memory usage in your application.</description>
			<author>Sergey Rogatnev</author>
			<guid>https://blog.rogatnev.net/posts/en/2025/11/List-initializer.html</guid>
			<pubDate>Thu, 27 Nov 2025 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;We're all used to write &lt;code&gt;new List&amp;lt;int&amp;gt; { 1, 2, 3, 4 }&lt;/code&gt; or &lt;code&gt;new int[] { 1, 2, 3, 4}&lt;/code&gt; to initialize collections with
values. Syntactically, they look similar, but the behavior is different, and you should be careful if you are worried
about
performance.&lt;/p&gt;
&lt;!--more--&gt;
&lt;h1 id="array"&gt;Array&lt;/h1&gt;
&lt;p&gt;As we know, &lt;a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/arrays"&gt;arrays&lt;/a&gt; contain a
sequence of fixed-size elements. Once created, their size cannot be changed for the entire lifetime of the array.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var array = new int[] { 1, 2, 3, 4};
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id="listt"&gt;List&amp;lt;T&amp;gt;&lt;/h1&gt;
&lt;p&gt;When we don't know the final size of a collection, or we need to add/remove elements during its life cycle, using &lt;a href="https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1?view=net-8.0"&gt;&lt;code&gt;List&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/a&gt; type is appropriate.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var list = new List&amp;lt;int&amp;gt;();
list.Add(1);
list.Add(2);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At first glance, it might seem like we can always use a list instead of an array — it has all the capabilities of an array, but it can also be dynamically resized.
But to decide whether to use a list, we need to understand more about its internal structure.&lt;/p&gt;
&lt;p&gt;Part of the source code:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public class List&amp;lt;T&amp;gt; : IList&amp;lt;T&amp;gt;, IList, IReadOnlyList&amp;lt;T&amp;gt;
{
    private const int DefaultCapacity = 4;

    internal T[] _items;
    internal int _size;

    private static readonly T[] s_emptyArray = new T[0];

    // Constructs a List. The list is initially empty and has a capacity
    // of zero. Upon adding the first element to the list the capacity is
    // increased to DefaultCapacity, and then increased in multiples of two
    // as required.
    public List()
    {
        _items = s_emptyArray;
    }
    ...

    public int Capacity
    {
        get =&amp;gt; _items.Length;
        set {...}
    }

    public int Count =&amp;gt; _size;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;_items&lt;/code&gt; - internal array for storing elements;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;_size&lt;/code&gt; - the number of elements in the array and the total size of the list;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Capacity&lt;/code&gt; - the size of the &lt;code&gt;_items&lt;/code&gt; array and the maximum number of elements that can fit into it without resizing.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="resizing-a-list"&gt;Resizing a list&lt;/h2&gt;
&lt;p&gt;In simple terms, we can say that a list is an array that can be resized as needed.&lt;/p&gt;
&lt;p&gt;Every time we try to add another item, the list checks if there is enough free space in &lt;code&gt;_items&lt;/code&gt;, otherwise it sets the new capacity:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;internal void Grow(int capacity)
{
    ...
    int newCapacity = _items.Length == 0 ? DefaultCapacity : 2 * _items.Length;
    ...
    Capacity = newCapacity;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's take a closer look at the property &lt;code&gt;Capacity&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;// Gets and sets the capacity of this list.  The capacity is the size of
// the internal array used to hold items.  When set, the internal
// array of the list is reallocated to the given capacity.
public int Capacity
{
    get =&amp;gt; _items.Length;
    set
    {
        if (value &amp;lt; _size)
        {
            ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_SmallCapacity);
        }

        if (value != _items.Length)
        {
            if (value &amp;gt; 0)
            {
                T[] newItems = new T[value];
                if (_size &amp;gt; 0)
                {
                    Array.Copy(_items, newItems, _size);
                }
                _items = newItems;
            }
            else
            {
                _items = s_emptyArray;
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, what do we see? Initially, each list is created with an empty internal array. After the first element is added,
the list creates a new array with 4 elements (&lt;code&gt;DefaultCapacity&lt;/code&gt; is 4). And when the current array is exhausted, a new one is created
with double the size, and all elements are copied.&lt;/p&gt;
&lt;h1 id="performance"&gt;Performance&lt;/h1&gt;
&lt;p&gt;What happens when we create a new list and initialize it with values?&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var list = new List&amp;lt;int&amp;gt; { 1, 2, 3, 4, 5 };
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This looks like array initialization, but it works completely differently.
According to the &lt;a href="https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers#collection-initializers"&gt;documentation&lt;/a&gt;, any type that implements &lt;code&gt;IEnumerable&lt;/code&gt; and has an &lt;code&gt;Add&lt;/code&gt; method can be used with a collection initializer.&lt;/p&gt;
&lt;p&gt;So, the previous example is just a shorthand form of successive calls to the &lt;code&gt;Add&lt;/code&gt; method:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var list = new List&amp;lt;int&amp;gt;();
list.Add(1);
list.Add(2);
list.Add(3);
list.Add(4);
list.Add(5);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The compiler simply transforms the short collection initializer and automatically adds the necessary calls. This can result in a performance penalty.&lt;/p&gt;
&lt;p&gt;Let's take a closer look at the process of adding 5 elements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Before the first call to &lt;code&gt;Add&lt;/code&gt; the list is empty, the internal array is empty.&lt;/li&gt;
&lt;li&gt;The first element added creates a new internal array with 4 elements.&lt;/li&gt;
&lt;li&gt;Elements 2, 3, 4 do not change anything when added.&lt;/li&gt;
&lt;li&gt;When we add the fifth element, the internal array is full and needs to be resized.
A new array of size 8 is created, all elements are copied from the previous array, and the fifth element is added.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, we end up with a list with 5 elements and an internal array with 8 elements. We've created two arrays, and the final one is wasting 37.5% of its space.
As you might have guessed, creating new arrays and copying elements results in memory allocation and takes additional time.&lt;/p&gt;
&lt;p&gt;This could be an unpleasant surprise in critical areas. Do we have a solution? Yes!&lt;/p&gt;
&lt;h2 id="capacity"&gt;Capacity&lt;/h2&gt;
&lt;p&gt;If we know or guess the finite size of the list, we can create it with an initial capacity (&lt;code&gt;Capacity&lt;/code&gt;).&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var list = new List&amp;lt;int&amp;gt;(5);
list.Add(1);
list.Add(2);
list.Add(3);
list.Add(4);
list.Add(5);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var list = new List&amp;lt;int&amp;gt;(5) { 1, 2, 3, 4, 5 };
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we immediately create one internal array of 5 elements, and there are no more unnecessary memory allocations.&lt;/p&gt;
&lt;h2 id="benchmark"&gt;Benchmark&lt;/h2&gt;
&lt;p&gt;Let's compare creating lists with and without initial capacity.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Full benchmark code. Click to expand.&lt;/summary&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;

namespace ListBenchmark
{
    [ShortRunJob(RuntimeMoniker.Net48)]
    [ShortRunJob(RuntimeMoniker.NetCoreApp31)]
    [ShortRunJob(RuntimeMoniker.Net80)]
    [ShortRunJob(RuntimeMoniker.Net10_0)]
    [MemoryDiagnoser]
    [HideColumns("Job", "Error", "StdDev", "Gen0")]
    public class InitListBenchmark
    {
        [BenchmarkCategory("One")]
        [Benchmark]
        public List&amp;lt;int&amp;gt; InitList1()
        {
            return new List&amp;lt;int&amp;gt; {1};
        }

        [BenchmarkCategory("One")]
        [Benchmark]
        public List&amp;lt;int&amp;gt; InitListWithSize1()
        {
            return new List&amp;lt;int&amp;gt;(1) {1};
        }

        [BenchmarkCategory("Five")]
        [Benchmark]
        public List&amp;lt;int&amp;gt; InitList5()
        {
            return new List&amp;lt;int&amp;gt; {1, 2, 3, 4, 5};
        }
        
        [BenchmarkCategory("Five")]
        [Benchmark]
        public List&amp;lt;int&amp;gt; InitListWithSize5()
        {
            return new List&amp;lt;int&amp;gt;(5) {1, 2, 3, 4, 5};
        }
        
        [BenchmarkCategory("Ten")]
        [Benchmark]
        public List&amp;lt;int&amp;gt; InitList10()
        {
            return new List&amp;lt;int&amp;gt; {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
        }
        
        [BenchmarkCategory("Ten")]
        [Benchmark]
        public List&amp;lt;int&amp;gt; InitListWithSize10()
        {
            return new List&amp;lt;int&amp;gt;(10) {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;pre&gt;&lt;code class="language-markdown"&gt;BenchmarkDotNet v0.15.6, Windows 10 (10.0.19045.6456/22H2/2022Update)
AMD Ryzen 7 7840H with Radeon 780M Graphics 3.80GHz, 1 CPU, 16 logical and 8 physical cores
.NET SDK 10.0.100
[Host]                      : .NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v4
ShortRun-.NET 10.0          : .NET 10.0.0 (10.0.0, 10.0.25.52411), X64 RyuJIT x86-64-v4
ShortRun-.NET 8.0           : .NET 8.0.22 (8.0.22, 8.0.2225.52707), X64 RyuJIT x86-64-v4
ShortRun-.NET Core 3.1      : .NET Core 3.1.32 (3.1.32, 4.700.22.55902), X64 RyuJIT VectorSize=256
ShortRun-.NET Framework 4.8 : .NET Framework 4.8.1 (4.8.9310.0), X64 RyuJIT VectorSize=256

IterationCount=3 LaunchCount=1 WarmupCount=3

| Method             | Runtime            |      Mean | Allocated |
|--------------------|--------------------|----------:|----------:|
| InitList1          | .NET Framework 4.8 | 13.904 ns |      80 B |
| InitListWithSize1  | .NET Framework 4.8 |  6.658 ns |      72 B |
| InitList1          | .NET Core 3.1      | 11.091 ns |      72 B |
| InitListWithSize1  | .NET Core 3.1      |  7.407 ns |      64 B |
| InitList1          | .NET 8.0           | 10.084 ns |      72 B |
| InitListWithSize1  | .NET 8.0           |  6.838 ns |      64 B |
| InitList1          | .NET 10.0          |  8.170 ns |      72 B |
| InitListWithSize1  | .NET 10.0          |  6.638 ns |      64 B |
| InitList5          | .NET Framework 4.8 | 31.298 ns |     136 B |
| InitListWithSize5  | .NET Framework 4.8 | 12.013 ns |      88 B |
| InitList5          | .NET Core 3.1      | 26.466 ns |     128 B |
| InitListWithSize5  | .NET Core 3.1      |  9.446 ns |      80 B |
| InitList5          | .NET 8.0           | 23.714 ns |     128 B |
| InitListWithSize5  | .NET 8.0           | 15.587 ns |      80 B |
| InitList5          | .NET 10.0          | 20.002 ns |     128 B |
| InitListWithSize5  | .NET 10.0          |  8.712 ns |      80 B |
| InitList10         | .NET Framework 4.8 | 53.488 ns |     225 B |
| InitListWithSize10 | .NET Framework 4.8 | 18.185 ns |     104 B |
| InitList10         | .NET Core 3.1      | 44.371 ns |     216 B |
| InitListWithSize10 | .NET Core 3.1      | 12.496 ns |      96 B |
| InitList10         | .NET 8.0           | 38.707 ns |     216 B |
| InitListWithSize10 | .NET 8.0           | 12.024 ns |      96 B |
| InitList10         | .NET 10.0          | 33.854 ns |     216 B |
| InitListWithSize10 | .NET 10.0          | 15.822 ns |      96 B |
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Setting the initial capacity prevents unnecessary memory allocations and results in better performance.
And there's no excess memory traffic. The more elements we add to the list, the greater the difference we see in the benchmarks.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/list/memory-allocation.png" alt="Difference in memory used"&gt;&lt;/p&gt;
&lt;h2 id="analyzer"&gt;Analyzer&lt;/h2&gt;
&lt;p&gt;If performance is critical for your project, you should pay attention to these situations. Automatic diagnostics can help.
I maintain a set of diagnostic tools based on Roslyn – &lt;a href="https://www.nuget.org/packages/Collections.Analyzer"&gt;Collections.Analyzer&lt;/a&gt;, and now it can detect
lists with a collection initializer and without an initial capacity.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/list/list-initializer.gif" alt="Automatically specify the initial list size"&gt;&lt;/p&gt;
&lt;p&gt;It's important to understand that the analyzer only sets the initial collection size to avoid unnecessary allocations during the initial collection phase.
He cannot predict how the size of the collection will change in the future.&lt;/p&gt;
&lt;h1 id="recommendations"&gt;Recommendations&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;If you need a static collection that you won't modify (add or remove elements), use arrays.&lt;/li&gt;
&lt;li&gt;If you are creating a list and exactly know its future size, set the initial capacity to that size.&lt;/li&gt;
&lt;li&gt;If you are creating a list and don't know its future size, set the &lt;strong&gt;expected&lt;/strong&gt; size.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="links"&gt;Links&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers#collection-initializers"&gt;Collection initializers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Backs/Collections.Analyzer"&gt;Collections.Analyzer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>Dapper: How caching can be harmful</title>
			<link>https://blog.rogatnev.net/posts/en/2025/09/Dapper.html</link>
			<description>Investigating the problem of high memory consumption when using caching in Dapper</description>
			<author>Sergey Rogatnev</author>
			<guid>https://blog.rogatnev.net/posts/en/2025/09/Dapper.html</guid>
			<pubDate>Sat, 06 Sep 2025 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;Dapper is a popular library that allows mapping objects from a database to C# types.
Unlike Entity Framework, it is not a full-fledged ORM, but it is very popular due to its minimalism.
In this article, I will explain how the default behavior can lead to a significant increase in memory consumption.&lt;/p&gt;
&lt;!--more--&gt;
&lt;h1 id="profiling"&gt;Profiling&lt;/h1&gt;
&lt;p&gt;This story began with one application at work. Metrics showed that over time it consumed more and more memory, although there were no obvious reasons for it.
I collected an application dump and opened it in a profiler.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/dapper/memory_profiler.png" alt="Memory profiler"&gt;
The problem was visible almost immediately — the application was holding 2 gigabytes of some strings in memory, related to Dapper.
The strings themselves contained the body of the SQL query. Let's study what objects in memory they are associated with.&lt;/p&gt;
&lt;h1 id="dapper-internals"&gt;Dapper internals&lt;/h1&gt;
&lt;p&gt;The profiler points to the &lt;code&gt;SqlMapper.Identity&lt;/code&gt; type and the associated &lt;code&gt;SqlMapper.CacheInfo&lt;/code&gt;. From the name, it's obvious that it is about caching.
Let's take a closer look at the creation of the &lt;code&gt;SqlMapper.Identity&lt;/code&gt; type during query execution:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;  private static async Task&amp;lt;int&amp;gt; ExecuteImplAsync(
    IDbConnection cnn,
    CommandDefinition command,
    object? param)
  {
    SqlMapper.CacheInfo cacheInfo = SqlMapper.GetCacheInfo(new SqlMapper.Identity(command.CommandText, new CommandType?(command.CommandTypeDirect), cnn, (Type) null, param?.GetType()), param, command.AddToCache);
    // ...
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As we can see, at the beginning of the method, we try to get something from the cache using &lt;code&gt;SqlMapper.Identity&lt;/code&gt; as a key, which contains the full text of the query in &lt;code&gt;command.CommandText&lt;/code&gt; and other parameters.&lt;/p&gt;
&lt;p&gt;The type stored in the cache is &lt;code&gt;SqlMapper.CacheInfo&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;  private class CacheInfo
  {
    private int hitCount;
    public SqlMapper.DeserializerState Deserializer { get; set; }
    public Func&amp;lt;DbDataReader, object&amp;gt;[]? OtherDeserializers { get; set; }
    public Action&amp;lt;IDbCommand, object?&amp;gt;? ParamReader { get; set; }
    public int GetHitCount() =&amp;gt; Interlocked.CompareExchange(ref this.hitCount, 0, 0);
    public void RecordHit() =&amp;gt; Interlocked.Increment(ref this.hitCount);
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a set of different deserializers associated with a specific SQL query, which allows Dapper to reuse objects for similar queries, speeding up data materialization.&lt;/p&gt;
&lt;p&gt;A fairly reasonable optimization. But why is our application holding such a large cache?&lt;/p&gt;
&lt;h1 id="sql-json"&gt;SQl + json = 🚩&lt;/h1&gt;
&lt;p&gt;The problem is how we construct the SQL query. For example, the method for getting users by a list of identifiers looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public async Task&amp;lt;ICollection&amp;lt;string&amp;gt;&amp;gt; SearchAsync(
    ThemeSearchRequest searchRequest, 
    int skip = 0,
    int take = int.MaxValue, 
    TimeSpan? timeout = null)
{
    var selectScript = @$"SELECT theme_base 
                          FROM [dbo].Themes 
                          {searchRequest.ToWhereExpression()}
                          GROUP BY theme_base
                          ORDER BY MAX(count_base) DESC, theme_base
                          OFFSET {skip} ROWS 
                          FETCH NEXT {take} ROWS ONLY";
    
        await using var connection = new SqlConnection(this.connectionString);
        
        var result = (await connection.QueryAsync&amp;lt;string&amp;gt;(
            selectScript,
            searchRequest.ToParametersWithValues(),
            commandTimeout: (int)(timeout?.TotalSeconds ?? DefaultTimeout.TotalSeconds))).AsList();
        
        return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;SearchAsync&lt;/code&gt; method forms a &lt;strong&gt;dynamic&lt;/strong&gt; query to the database — some search conditions, pagination. And that's the problem.
Almost every query will be unique, and according to Dapper's basic logic, it will be cached for fast materialization in the future.&lt;/p&gt;
&lt;p&gt;The same is written in the &lt;a href="https://github.com/DapperLib/Dapper#limitations-and-caveats"&gt;documentation&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Dapper caches information about every query it runs, this allows it to materialize objects quickly and process parameters quickly. The current implementation caches this information in a ConcurrentDictionary object. Statements that are only used once are routinely flushed from this cache. Still, if you are generating SQL strings on the fly without using parameters it is possible you may hit memory issues.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id="dapper-caching"&gt;Dapper caching&lt;/h1&gt;
&lt;p&gt;Let's take a closer look at the caching mechanism in Dapper. We have already found out that each SQL query is used as a key and stores additional information for fast materialization of objects.
Will this cache grow indefinitely? Let's look at the &lt;code&gt;SqlMapper&lt;/code&gt; code:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;private static bool TryGetQueryCache(Identity key, [NotNullWhen(true)] out CacheInfo? value)
{
    if (_queryCache.TryGetValue(key, out value!))
    {
        value.RecordHit();
        return true;
    }
    value = null;
    return false;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;TryGetQueryCache&lt;/code&gt; method searches for data in the cache and, if successful, increments the hit counter — the number of times the data has been returned for this key.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;private const int COLLECT_PER_ITEMS = 1000;
private static void SetQueryCache(Identity key, CacheInfo value)
{
    if (Interlocked.Increment(ref collect) == COLLECT_PER_ITEMS)
    {
        CollectCacheGarbage();
    }
    _queryCache[key] = value;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;SetQueryCache&lt;/code&gt; method tries to add data to the cache until it reaches a size of 1000 elements.
When this limit is reached, the &lt;code&gt;CollectCacheGarbage()&lt;/code&gt; method is called:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;private const int COLLECT_HIT_COUNT_MIN = 0;
private static void CollectCacheGarbage()
{
    try
    {
        foreach (var pair in _queryCache)
        {
            if (pair.Value.GetHitCount() &amp;lt;= COLLECT_HIT_COUNT_MIN)
            {
                _queryCache.TryRemove(pair.Key, out var _);
            }
        }
    }
    finally
    {
        Interlocked.Exchange(ref collect, 0);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It tries to remove all cached items that have never been accessed. In this process, the &lt;code&gt;GetHitCount()&lt;/code&gt; method resets the internal hit counter:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public int GetHitCount() { return Interlocked.CompareExchange(ref hitCount, 0, 0); }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Thus, after &lt;code&gt;CollectCacheGarbage()&lt;/code&gt; is run, the value of all &lt;code&gt;hitCount&lt;/code&gt;s is reset, which allows finding unused elements during the next cleanup.
But in the case of active use of dynamic SQL queries, it is highly likely that all queries will be used once and will be constantly evicted from the cache.&lt;/p&gt;
&lt;p&gt;Using one-time queries not only hits your application's memory but also creates an additional load on the SQL server.
It will have to parse, compile, and build an execution plan for each new query.&lt;/p&gt;
&lt;h1 id="solution"&gt;Solution&lt;/h1&gt;
&lt;p&gt;The correct solution here would be to use parameterized queries so that a single query will be reused many times, including on the SQL server:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public async Task&amp;lt;ICollection&amp;lt;TaskRules&amp;gt;&amp;gt; GetTaskRulesAsync(long taskId)
{
    var sql = $@"SELECT * FROM TaskRules WHERE TaskID = @taskId";
    await using var connection = new SqlConnection(this.connectionString);
    await connection.OpenAsync();
    var result = await connection
        .QueryAsync&amp;lt;TaskRules&amp;gt;(sql, new { taskId }, commandTimeout: 180);

    return result.AsList();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;GetTaskRulesAsync&lt;/code&gt; method uses a parameterized SQL query, where data is selected by &lt;code&gt;@taskId&lt;/code&gt;.
This allows using caching at all levels — in the application and on the SQL server.&lt;/p&gt;
&lt;p&gt;A quick solution might be to disable caching for a specific query — this can be done by managing &lt;code&gt;CommandFlags&lt;/code&gt; when creating the command:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;new CommandDefinition(sql, CommandFlags.NoCache)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This way, "one-time" queries will not get into the cache and clutter up the memory.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Many solutions try to optimize frequently used scenarios through strict rules or some of their own heuristics.
Such important rules are usually described somewhere prominently. In Dapper's case — right in the Readme.md.&lt;/p&gt;
&lt;h1 id="recommendations"&gt;Recommendations&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Perform periodic profiling, see how applications use memory. For example, using &lt;a href="https://www.jetbrains.com/dotmemory/"&gt;dotMemory&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Use parameterized queries for working with SQL.&lt;/li&gt;
&lt;li&gt;If any solution uses caching, make sure it is useful for you.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>String of Digits</title>
			<link>https://blog.rogatnev.net/posts/en/2025/07/String-of-Digits.html</link>
			<description>We consider different approaches to checking digital strings in C# - comparison of methods, efficiency and ready-made examples.</description>
			<author>Sergey Rogatnev</author>
			<guid>https://blog.rogatnev.net/posts/en/2025/07/String-of-Digits.html</guid>
			<pubDate>Sat, 05 Jul 2025 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;A popular task is to check if a string contains only digits. For example, you need to check if the user entered the
correct phone number, index or a tax code of an organization.
There are several ways to solve this task, which differ in their efficiency. Let's take a look at the most popular ones.&lt;/p&gt;
&lt;!--more--&gt;
&lt;h1 id="regex"&gt;Regex&lt;/h1&gt;
&lt;p&gt;Probably the most popular way to solve this task is to use a regular expression.
It's straightforward and easy to use an expression &lt;code&gt;^[0-9]*$&lt;/code&gt; (or &lt;code&gt;^\d*$&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Below is a naive implementation of a regular expression check:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;Regex regex = new Regex("^[0-9]*$");
var value = "123456789000";
var isValid = regex.IsMatch(value);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I'm sure someone sees the problem. This implementation is only suitable for a one-time launch.
In production code, when you check thousands of strings, this solution will not be efficient.
.NET provides the ability to compile a regular expression at runtime, by using the &lt;code&gt;RegexOptions.Compiled&lt;/code&gt; option:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;Regex regex = new Regex("^[0-9]*$", RegexOptions.Compiled);
var value = "123456789000";
var isValid = regex.IsMatch(value);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Calling a constructor with this option will generate IL code that will be called through &lt;code&gt;DynamicMethod&lt;/code&gt; inside
&lt;code&gt;Regex.IsMatch&lt;/code&gt;, which will be faster than the usual regular expression processing.
The downside is that it takes longer to create a &lt;code&gt;Regex&lt;/code&gt; object due to the time spent on compilation at runtime, but
this quickly pays off with repeated use.&lt;/p&gt;
&lt;p&gt;Let's compare both solutions.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Benchmark code. Click to expand.&lt;/summary&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;[MemoryDiagnoser]
[SimpleJob(RuntimeMoniker.Net90)]
[SimpleJob(RuntimeMoniker.Net60)]
[SimpleJob(RuntimeMoniker.NetCoreApp31)]
[SimpleJob(RuntimeMoniker.Net48)]
[HideColumns("Job", "Error", "StdDev", "Gen0")]
public partial class DigitsBenchmarks
{
    private static string value = "123456789000";

    private static readonly Regex regex = new Regex("^[0-9]*$");
    private static readonly Regex compiledRegex = new Regex("^[0-9]*$", RegexOptions.Compiled);

    [Benchmark]
    public bool Regex()
    {
        return regex.IsMatch(value);
    }
    
    [Benchmark]
    public bool CompiledRegex()
    {
        return compiledRegex.IsMatch(value);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;p&gt;Results:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Runtime&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Mean&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Median&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Allocated&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Regex&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td style="text-align: right;"&gt;165.4417 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;166.2537 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CompiledRegex&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td style="text-align: right;"&gt;115.9377 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;115.9720 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Regex&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td style="text-align: right;"&gt;118.1540 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;118.1887 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CompiledRegex&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td style="text-align: right;"&gt;89.7392 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;89.6514 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Regex&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;57.8247 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;57.8031 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CompiledRegex&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;21.2952 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;21.2616 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Regex&lt;/td&gt;
&lt;td&gt;.NET 9.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;47.2579 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;47.3506 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CompiledRegex&lt;/td&gt;
&lt;td&gt;.NET 9.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;24.2419 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;24.2547 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/string-of-digits/regex_compiled.png" alt="Regex &amp;amp; Compiled Regex"&gt;&lt;/p&gt;
&lt;p&gt;The advantage of using compiled expressions is quite obvious. Also, with each new version of .NET, the contribution of
developers to performance is more visible. This is another argument in favor of upgrading to modern versions of the .NET
Framework.&lt;/p&gt;
&lt;h1 id="regex-source-generators"&gt;Regex source generators&lt;/h1&gt;
&lt;p&gt;Again, compiling regular expressions has one drawback - creating a &lt;code&gt;Regex&lt;/code&gt; object at runtime will take some time. Is it
possible to get rid of this?&lt;/p&gt;
&lt;p&gt;Starting with .NET 7, this capability is available
through &lt;a href="https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/#source-generators"&gt;source generators&lt;/a&gt;.
Strictly speaking, source generators appeared in .NET 5, but the regular expression solution was implemented only in the
seventh version.&lt;/p&gt;
&lt;p&gt;Source generators allow you to create C# code at &lt;strong&gt;compile&lt;/strong&gt; time, which means you can view and debug it as if it was
your own code.
And regular expressions can be converted to C# code at compile time! .NET has a special attribute for this -
&lt;code&gt;GeneratedRegex&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;namespace DigitBenchmark
{
    public partial class DigitsBenchmarks
    {
        private static readonly Regex generatedRegex = GenerateRegex();
        
        [GeneratedRegex("^[0-9]*$")]
        private static partial Regex GenerateRegex();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's figure out what's going on here.&lt;/p&gt;
&lt;p&gt;First, we need to mark our &lt;code&gt;DigitsBenchmarks&lt;/code&gt; class as &lt;code&gt;partial&lt;/code&gt;, since some of the generated code for this class will
be in another file.
Next we need to create a &lt;code&gt;partial&lt;/code&gt; method that will return an object of type &lt;code&gt;Regex&lt;/code&gt; and mark it with the
&lt;code&gt;GeneratedRegex&lt;/code&gt; attribute specifying the regular expression pattern.
You don't need to specify the &lt;code&gt;RegexOptions.Compiled&lt;/code&gt; option, it will be ignored.&lt;/p&gt;
&lt;p&gt;The implementation of the &lt;code&gt;GenerateRegex&lt;/code&gt; method will be in another file. You can find it in the project and view the
source code:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;namespace DigitBenchmark
{
    partial class DigitsBenchmarks
    {
        /// &amp;lt;remarks&amp;gt;
        /// Pattern:&amp;lt;br/&amp;gt;
        /// &amp;lt;code&amp;gt;^[0-9]*$&amp;lt;/code&amp;gt;&amp;lt;br/&amp;gt;
        /// Explanation:&amp;lt;br/&amp;gt;
        /// &amp;lt;code&amp;gt;
        /// ○ Match if at the beginning of the string.&amp;lt;br/&amp;gt;
        /// ○ Match a character in the set [0-9] atomically any number of times.&amp;lt;br/&amp;gt;
        /// ○ Match if at the end of the string or if before an ending newline.&amp;lt;br/&amp;gt;
        /// &amp;lt;/code&amp;gt;
        /// &amp;lt;/remarks&amp;gt;
        [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Text.RegularExpressions.Generator", "8.0.12.21506")]
        private static partial global::System.Text.RegularExpressions.Regex GenerateRegex() =&amp;gt; global::System.Text.RegularExpressions.Generated.GenerateRegex_0.Instance;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, the file with the same class was automatically created and contains the implementation of our method for
generating a regular expression.&lt;/p&gt;
&lt;p&gt;And then we can use this &lt;code&gt;Regex&lt;/code&gt; object as usual. Since this is real C# code, there is no need to generate anything at
runtime, so there is no point in specifying the &lt;code&gt;RegexOptions.Compiled&lt;/code&gt; option.&lt;/p&gt;
&lt;p&gt;What benefit do we get from this? My benchmarks do not include .NET 7 and 8, let's compare the performance of the latest
one at the moment:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Runtime&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Mean&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Median&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Allocated&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Regex&lt;/td&gt;
&lt;td&gt;.NET 9.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;47.2579 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;47.3506 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CompiledRegex&lt;/td&gt;
&lt;td&gt;.NET 9.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;24.2419 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;24.2547 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GeneratedRegex&lt;/td&gt;
&lt;td&gt;.NET 9.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;17.2548 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;17.2603 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;We see that the time has been reduced by almost 30%! The compiler has much more opportunity to optimize the source
code at the compilation stage than at runtime.&lt;/p&gt;
&lt;h1 id="char.isdigit"&gt;char.IsDigit&lt;/h1&gt;
&lt;p&gt;Another popular way is to use the static &lt;code&gt;char.IsDigit&lt;/code&gt; method in combination with the LINQ &lt;code&gt;All&lt;/code&gt; method:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var value = "123456789000";
var isValid = value.All(char.IsDigit);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's check how good this method is in terms of performance.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Benchmark code. Click to expand.&lt;/summary&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;[MemoryDiagnoser]
[SimpleJob(RuntimeMoniker.Net90)]
[SimpleJob(RuntimeMoniker.Net60)]
[SimpleJob(RuntimeMoniker.NetCoreApp31)]
[SimpleJob(RuntimeMoniker.Net48)]
[HideColumns("Job", "Error", "StdDev", "Gen0")]
public partial class DigitsBenchmarks
{
    [Benchmark]
    public bool LinqCharIsDigit()
    {
        return value.All(char.IsDigit);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;p&gt;Results:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Runtime&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Mean&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Median&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Allocated&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;LinqCharIsDigit&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td style="text-align: right;"&gt;92.1679 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;92.2549 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;96 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LinqCharIsDigit&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td style="text-align: right;"&gt;72.0987 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;72.6419 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;96 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LinqCharIsDigit&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;74.2609 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;74.4256 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;96 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LinqCharIsDigit&lt;/td&gt;
&lt;td&gt;.NET 9.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;31.0294 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;31.0501 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;32 B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;And let's compare this method with previous solutions.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/string-of-digits/regex_isdigit.png" alt="Regex vs char.IsDigit"&gt;&lt;/p&gt;
&lt;p&gt;If in older versions of the framework, LINQ validation and the &lt;code&gt;IsDigit&lt;/code&gt; method has an advantage over regular
expressions, then later we see that such an implementation starts to lose.&lt;/p&gt;
&lt;p&gt;Also, please note that each call results in the allocation of additional memory. This value consists of two parts:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Creating a lambda expression in a method parameter &lt;code&gt;All(c =&amp;gt; char.IsDigit(c))&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Creating an iterator inside the &lt;code&gt;All&lt;/code&gt; method.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It is noteworthy that .NET 9 allocates three times less memory.
Before .NET 9, the &lt;code&gt;All&lt;/code&gt; method consisted of a &lt;code&gt;foreach&lt;/code&gt; loop with a condition:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public static bool All&amp;lt;TSource&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; source, Func&amp;lt;TSource, bool&amp;gt; predicate)
{
  //...
  foreach (TSource source1 in source)
  {
    if (!predicate(source1))
      return false;
  }
  return true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But in .NET 9, an important optimization was added:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public static bool All&amp;lt;TSource&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; source, Func&amp;lt;TSource, bool&amp;gt; predicate)
{
  //...
  ReadOnlySpan&amp;lt;TSource&amp;gt; span;
  if (source.TryGetSpan&amp;lt;TSource&amp;gt;(out span))
  {
    ReadOnlySpan&amp;lt;TSource&amp;gt; readOnlySpan = span;
    for (int index = 0; index &amp;lt; readOnlySpan.Length; ++index)
    {
      TSource source1 = readOnlySpan[index];
      if (!predicate(source1))
        return false;
    }
  }
  else
  {
    foreach (TSource source2 in source)
    {
      if (!predicate(source2))
        return false;
    }
  }
  return true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of an unconditional &lt;code&gt;foreach&lt;/code&gt; loop, the &lt;code&gt;All&lt;/code&gt; method tries to get a &lt;code&gt;ReadOnlySpan&lt;/code&gt; - a read-safe contiguous
block of memory - from the source.
And then a simple &lt;code&gt;for&lt;/code&gt; loop is used, which does not lead to the creation of an iterator, reducing the amount of
additional memory.
You can get rid of this completely by rewriting the &lt;code&gt;All&lt;/code&gt; method to a regular loop:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public bool ForIsDigit()
{
    for (var i = 0; i &amp;lt; value.Length; i++)
    {
        if (!char.IsDigit(value[i]))
            return false;
    }

    return true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In addition to the absence of unnecessary memory traffic, this solution is rapid.
&lt;img src="https://blog.rogatnev.net/img/string-of-digits/linq_for.png" alt="LINQ vs for"&gt;&lt;/p&gt;
&lt;h1 id="what-is-a-digit"&gt;What is a digit?&lt;/h1&gt;
&lt;p&gt;It seems that we have found the optimal solution for checking a string for the presence of only digits.
But try to guess what the following code will output:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;Console.WriteLine(char.IsDigit('0'));
Console.WriteLine(char.IsDigit('a'));
Console.WriteLine(char.IsDigit('٨'));
Console.WriteLine(char.IsDigit('৯'));
&lt;/code&gt;&lt;/pre&gt;
&lt;details&gt;
&lt;summary&gt;Click to find out the answer.&lt;/summary&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;Console.WriteLine(char.IsDigit('0')); //True
Console.WriteLine(char.IsDigit('a')); //False
Console.WriteLine(char.IsDigit('٨')); //True
Console.WriteLine(char.IsDigit('৯')); //True
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;p&gt;I think the result surprises you. But there is nothing unusual about it, the &lt;code&gt;IsDigit&lt;/code&gt; method considers as
digits not only the usual symbols from the set &lt;code&gt;0-9&lt;/code&gt;, but also all other symbols that are related to digits in the
Unicode encoding. And there are actually a lot of them.
This can be a problem if you rely on such a check in your business code.&lt;/p&gt;
&lt;p&gt;I think this is the reason for the new &lt;code&gt;char.IsAsciiDigit&lt;/code&gt; method to appear starting with .NET 7. Now it really only
checks characters from the &lt;code&gt;0-9&lt;/code&gt; set.&lt;/p&gt;
&lt;p&gt;Its implementation is very similar to manually checking each character in the loop, let's compare both solutions:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;[Benchmark]
public bool ForCompare()
{
    for (var i = 0; i &amp;lt; value.Length; i++)
    {
        if (value[i] &amp;lt; '0' || value[i] &amp;gt; '9')
            return false;
    }

    return true;
}

[Benchmark]
public bool ForIsAsciiDigit()
{
    for (var i = 0; i &amp;lt; value.Length; i++)
    {
        if (!char.IsAsciiDigit(value[i]))
            return false;
    }

    return true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Runtime&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Mean&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Median&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Allocated&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ForCompare&lt;/td&gt;
&lt;td&gt;.NET 9.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.8587 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.8656 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ForIsAsciiDigit&lt;/td&gt;
&lt;td&gt;.NET 9.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.7515 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.4976 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Both methods show equivalent results.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;We've looked at several different ways to check whether a string consists only of digits or not.
And it's important to understand the specifics of how some of them work, because in addition to trivial performance
issues, you can get errors in your business logic if you don't check your input data well enough.&lt;/p&gt;
&lt;p&gt;Recommendations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you write your applications for .NET 7 or higher, then use the generated regular expressions. Otherwise, specify
the &lt;code&gt;RegexOptions.Compiled&lt;/code&gt; option.&lt;/li&gt;
&lt;li&gt;If you write your applications for .NET 7, use the &lt;code&gt;char.IsAsciiDigit&lt;/code&gt; method to check characters. Otherwise, it is
better to write the check yourself.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="links"&gt;Links&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/dotnet/standard/base-types/compilation-and-reuse-in-regular-expressions"&gt;Compilation and reuse in regular expressions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/dotnet/api/system.char.isdigit?view=net-9.0"&gt;char.IsDigit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/dotnet/api/system.char.isasciidigit?view=net-9.0"&gt;char.IsAsciiDigit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;details&gt;
&lt;summary&gt;Full benchmark code. Click to expand.&lt;/summary&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;using System.Linq;
using System.Text.RegularExpressions;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;

namespace DigitBenchmark
{
    [MemoryDiagnoser]
    [SimpleJob(RuntimeMoniker.Net90)]
    [SimpleJob(RuntimeMoniker.Net60)]
    [SimpleJob(RuntimeMoniker.NetCoreApp31)]
    [SimpleJob(RuntimeMoniker.Net48)]
    [HideColumns("Job", "Error", "StdDev", "Gen0")]
    public partial class DigitsBenchmarks
    {
        private static string value = "123456789000";

        private static readonly Regex regex = new Regex("^[0-9]*$");
        private static readonly Regex compiledRegex = new Regex("^[0-9]*$", RegexOptions.Compiled);
        private static readonly Regex generatedRegex = GenerateRegex();

        [GeneratedRegex("^[0-9]*$")]
        private static partial Regex GenerateRegex();

        [Benchmark]
        public bool Regex()
        {
            return regex.IsMatch(value);
        }
        
        [Benchmark]
        public bool CompiledRegex()
        {
            return compiledRegex.IsMatch(value);
        }

        [Benchmark]
        public bool GeneratedRegex()
        {
            return generatedRegex.IsMatch(value);
        }

        [Benchmark]
        public bool LinqCharIsDigit()
        {
            return value.All(char.IsDigit);
        }

        [Benchmark]
        public bool ForCompare()
        {
            for (var i = 0; i &amp;lt; value.Length; i++)
            {
                if (value[i] &amp;lt; '0' || value[i] &amp;gt; '9')
                    return false;
            }

            return true;
        }

        [Benchmark]
        public bool ForIsDigit()
        {
            for (var i = 0; i &amp;lt; value.Length; i++)
            {
                if (!char.IsDigit(value[i]))
                    return false;
            }

            return true;
        }

        [Benchmark]
        public bool ForIsAsciiDigit()
        {
            for (var i = 0; i &amp;lt; value.Length; i++)
            {
                if (!char.IsAsciiDigit(value[i]))
                    return false;
            }

            return true;
        }

        [Benchmark]
        public bool LinqCharIsAsciiDigit()
        {
            return value.All(char.IsAsciiDigit);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;Full results. Click to expand.&lt;/summary&gt;
&lt;pre&gt;&lt;code&gt;BenchmarkDotNet v0.15.0, Windows 10 (10.0.19045.5917/22H2/2022Update)
AMD Ryzen 7 7840H with Radeon 780M Graphics 3.80GHz, 1 CPU, 16 logical and 8 physical cores
.NET SDK 9.0.204
  [Host]             : .NET 9.0.5 (9.0.525.21509), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
  .NET 6.0           : .NET 6.0.36 (6.0.3624.51421), X64 RyuJIT AVX2
  .NET 9.0           : .NET 9.0.5 (9.0.525.21509), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
  .NET Core 3.1      : .NET Core 3.1.32 (CoreCLR 4.700.22.55902, CoreFX 4.700.22.56512), X64 RyuJIT AVX2
  .NET Framework 4.8 : .NET Framework 4.8.1 (4.8.9310.0), X64 RyuJIT VectorSize=256
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Runtime&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Mean&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Median&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Allocated&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Regex&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td style="text-align: right;"&gt;165.4417 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;166.2537 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CompiledRegex&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td style="text-align: right;"&gt;115.9377 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;115.9720 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GeneratedRegex&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td style="text-align: right;"&gt;N/A&lt;/td&gt;
&lt;td style="text-align: right;"&gt;N/A&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LinqCharIsDigit&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td style="text-align: right;"&gt;92.1679 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;92.2549 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;96 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ForCompare&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.8703 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.8602 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ForIsDigit&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td style="text-align: right;"&gt;8.3920 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;8.3952 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ForIsAsciiDigit&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td style="text-align: right;"&gt;N/A&lt;/td&gt;
&lt;td style="text-align: right;"&gt;N/A&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LinqCharIsAsciiDigit&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td style="text-align: right;"&gt;N/A&lt;/td&gt;
&lt;td style="text-align: right;"&gt;N/A&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Regex&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td style="text-align: right;"&gt;118.1540 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;118.1887 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CompiledRegex&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td style="text-align: right;"&gt;89.7392 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;89.6514 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GeneratedRegex&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td style="text-align: right;"&gt;N/A&lt;/td&gt;
&lt;td style="text-align: right;"&gt;N/A&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LinqCharIsDigit&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td style="text-align: right;"&gt;72.0987 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;72.6419 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;96 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ForCompare&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5.3070 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5.3077 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ForIsDigit&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td style="text-align: right;"&gt;9.0998 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;9.1106 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ForIsAsciiDigit&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td style="text-align: right;"&gt;N/A&lt;/td&gt;
&lt;td style="text-align: right;"&gt;N/A&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LinqCharIsAsciiDigit&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td style="text-align: right;"&gt;N/A&lt;/td&gt;
&lt;td style="text-align: right;"&gt;N/A&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Regex&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;57.8247 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;57.8031 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CompiledRegex&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;21.2952 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;21.2616 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GeneratedRegex&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;N/A&lt;/td&gt;
&lt;td style="text-align: right;"&gt;N/A&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LinqCharIsDigit&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;74.2609 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;74.4256 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;96 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ForCompare&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5.6198 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5.5922 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ForIsDigit&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;10.3160 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;10.1789 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ForIsAsciiDigit&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;N/A&lt;/td&gt;
&lt;td style="text-align: right;"&gt;N/A&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LinqCharIsAsciiDigit&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;N/A&lt;/td&gt;
&lt;td style="text-align: right;"&gt;N/A&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Regex&lt;/td&gt;
&lt;td&gt;.NET 9.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;47.2579 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;47.3506 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CompiledRegex&lt;/td&gt;
&lt;td&gt;.NET 9.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;24.2419 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;24.2547 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GeneratedRegex&lt;/td&gt;
&lt;td&gt;.NET 9.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;17.2548 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;17.2603 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LinqCharIsDigit&lt;/td&gt;
&lt;td&gt;.NET 9.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;31.0294 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;31.0501 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;32 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ForCompare&lt;/td&gt;
&lt;td&gt;.NET 9.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.8587 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.8656 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ForIsDigit&lt;/td&gt;
&lt;td&gt;.NET 9.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;7.1207 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;7.7792 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ForIsAsciiDigit&lt;/td&gt;
&lt;td&gt;.NET 9.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.7515 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.4976 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LinqCharIsAsciiDigit&lt;/td&gt;
&lt;td&gt;.NET 9.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;32.6861 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;32.5483 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;32 B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;N/A&lt;/code&gt; mark is given to methods that do not exist in the current version of the framework.&lt;/li&gt;
&lt;/ul&gt;
&lt;/details&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>Any() vs Count: part 2</title>
			<link>https://blog.rogatnev.net/posts/en/2025/05/Any-vs-Count-part-2.html</link>
			<description>Dive deep into the performance comparison of Any() versus Count in .NET for different collection types and framework versions, including concurrent collections. Includes comprehensive benchmarks and recommendations.</description>
			<author>Sergey Rogatnev</author>
			<guid>https://blog.rogatnev.net/posts/en/2025/05/Any-vs-Count-part-2.html</guid>
			<pubDate>Sun, 11 May 2025 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;In &lt;a href="xref:2018-06-16-LINQ-Any-vs-Count"&gt;Part 1&lt;/a&gt;, we compared &lt;code&gt;Any()&lt;/code&gt; and &lt;code&gt;Count&lt;/code&gt; methods for different collections and proposed optimization approaches.&lt;/p&gt;
&lt;!--more--&gt;
&lt;h1 id="array"&gt;Array&lt;/h1&gt;
&lt;p&gt;Let's start with the simplest collection type - arrays.
We'll check if there's any difference between &lt;code&gt;array.Any()&lt;/code&gt; and &lt;code&gt;array.Length != 0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The following configuration is used throughout:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;BenchmarkDotNet v0.14.0, Windows 10 (10.0.19045.5796/22H2/2022Update)
AMD Ryzen 7 7840H with Radeon 780M Graphics, 1 CPU, 16 logical and 8 physical cores
.NET SDK 9.0.203
  [Host]             : .NET 9.0.4 (9.0.425.16305), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
  .NET 5.0           : .NET 5.0.17 (5.0.1722.21314), X64 RyuJIT AVX2
  .NET 6.0           : .NET 6.0.36 (6.0.3624.51421), X64 RyuJIT AVX2
  .NET 8.0           : .NET 8.0.15 (8.0.1525.16413), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
  .NET 9.0           : .NET 9.0.4 (9.0.425.16305), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
  .NET Core 3.1      : .NET Core 3.1.32 (CoreCLR 4.700.22.55902, CoreFX 4.700.22.56512), X64 RyuJIT AVX2
  .NET Framework 4.8 : .NET Framework 4.8.1 (4.8.9310.0), X64 RyuJIT VectorSize=256

&lt;/code&gt;&lt;/pre&gt;
&lt;details&gt;
&lt;summary&gt;Benchmark code. Click to expand.&lt;/summary&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;using System.Linq;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;

namespace AnyVsCount
{
    [MemoryDiagnoser]
    [SimpleJob(RuntimeMoniker.Net90)]
    [SimpleJob(RuntimeMoniker.Net80)]
    [SimpleJob(RuntimeMoniker.Net60)]
    [SimpleJob(RuntimeMoniker.Net50)]
    [SimpleJob(RuntimeMoniker.NetCoreApp31)]
    [SimpleJob(RuntimeMoniker.Net48)]
    [HideColumns("Job", "Error", "StdDev", "Gen0")]
    public class ArrayBenchmark
    {
        [Params(10, 10000)]
        public int N;

        private int[] array;

        [GlobalSetup]
        public void SetUp()
        {
            array = new int[N];
        }

        [Benchmark]
        public bool ArrayAny()
        {
            return array.Any();
        }

        [Benchmark]
        public bool ArrayCount()
        {
            return array.Length != 0;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h3 id="results"&gt;Results&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Runtime&lt;/th&gt;
&lt;th&gt;N&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Mean&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Median&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Allocated&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ArrayAny&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;8.7962 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;8.8213 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;32 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayCount&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0073 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0028 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayAny&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;8.0450 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;8.0462 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;32 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayCount&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0433 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0399 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayAny&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6.2080 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6.1852 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayCount&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0030 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0000 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayAny&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6.0673 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6.0671 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayCount&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0135 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0135 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayAny&lt;/td&gt;
&lt;td&gt;.NET 8.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5.0980 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5.2591 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayCount&lt;/td&gt;
&lt;td&gt;.NET 8.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0000 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0000 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayAny&lt;/td&gt;
&lt;td&gt;.NET 9.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.1643 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.1216 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayCount&lt;/td&gt;
&lt;td&gt;.NET 9.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0030 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0034 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayAny&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;9.0093 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;9.0043 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;32 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayCount&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0123 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0137 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayAny&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;7.9624 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;7.7794 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;32 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayCount&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0311 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0295 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayAny&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.8357 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.8352 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayCount&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0008 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0007 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayAny&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6.0913 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6.0747 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayCount&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0147 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0153 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayAny&lt;/td&gt;
&lt;td&gt;.NET 8.0&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.7691 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.7521 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayCount&lt;/td&gt;
&lt;td&gt;.NET 8.0&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0110 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0078 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayAny&lt;/td&gt;
&lt;td&gt;.NET 9.0&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.2933 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.2906 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayCount&lt;/td&gt;
&lt;td&gt;.NET 9.0&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0121 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0109 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;Values close to zero (e.g., 0.0000 ns) should be considered negligible — they are within measurement error.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Legend for all tables:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  N         : Value of the 'N' parameter
  Mean      : Arithmetic mean of all measurements
  Median    : Value separating the higher half of all measurements (50th percentile)
  Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)
  1 ns      : 1 Nanosecond (0.000000001 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For arrays, results are nearly independent of size, so we'll focus on &lt;code&gt;N = 10000&lt;/code&gt;.
Directly accessing the &lt;code&gt;Length&lt;/code&gt; property is at least 2 orders of scale faster than &lt;code&gt;Any()&lt;/code&gt;.
However, in generic code working with &lt;code&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt;, we often use:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public bool IsEmpty&amp;lt;T&amp;gt;(IEnumerable&amp;lt;T&amp;gt; collection)
{
    return !collection.Any();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's analyze why &lt;code&gt;Any()&lt;/code&gt; performance varies across .NET versions.
From .NET 4.8 to 9.0, &lt;code&gt;Any()&lt;/code&gt; execution time decreased 4x:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/any-part2/any1.png" alt="Method Any performance"&gt;&lt;/p&gt;
&lt;p&gt;In .NET 4.8 and Core 3.1, &lt;code&gt;Any()&lt;/code&gt; creates an iterator and checks &lt;code&gt;MoveNext()&lt;/code&gt;, allocating 32 bytes:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public static bool Any&amp;lt;TSource&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; source)
{
    if (source == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
    }

    using (IEnumerator&amp;lt;TSource&amp;gt; e = source.GetEnumerator())
    {
        return e.MoveNext();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Starting with .NET 5, optimizations were introduced:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public static bool Any&amp;lt;TSource&amp;gt;(this IEnumerable&amp;lt;TSource&amp;gt; source)
{
    if (source == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
    }

    if (source is ICollection&amp;lt;TSource&amp;gt; collectionoft)
    {
        return collectionoft.Count != 0;
    }
    else if (source is IIListProvider&amp;lt;TSource&amp;gt; listProv)
    {
        int count = listProv.GetCount(onlyIfCheap: true);
        if (count &amp;gt;= 0)
        {
            return count != 0;
        }
    }
    else if (source is ICollection collection)
    {
        return collection.Count != 0;
    }

    using (IEnumerator&amp;lt;TSource&amp;gt; e = source.GetEnumerator())
    {
        return e.MoveNext();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This solution is similar to the one we used in &lt;a href="xref:2018-06-16-LINQ-Any-vs-Count"&gt;Part 1&lt;/a&gt;.
Firstly it checks interface &lt;code&gt;ICollection&amp;lt;T&amp;gt;&lt;/code&gt;, that has property &lt;code&gt;Count&lt;/code&gt;. And then we see usage of a new interface &lt;code&gt;IIListProvider&amp;lt;TSource&amp;gt;&lt;/code&gt; (internal interface of .NET to optimize LINQ methods):&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;internal interface IIListProvider&amp;lt;TElement&amp;gt; : IEnumerable&amp;lt;TElement&amp;gt;
{
    TElement[] ToArray();

    List&amp;lt;TElement&amp;gt; ToList();

    int GetCount(bool onlyIfCheap);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The interface &lt;code&gt;IIListProvider&amp;lt;TSource&amp;gt;&lt;/code&gt; is part of a major rework of LINQ that has resulted in significant optimizations noticeable even in the simple &lt;code&gt;Any()&lt;/code&gt; method.
Interestingly, in .NET 6 the performance has degraded somewhat, although the implementation of the &lt;code&gt;Any()&lt;/code&gt; method has not changed.&lt;/p&gt;
&lt;p&gt;In the eighth version of .NET, the code that calculates the size of an enumeration (without performing the enumeration) was moved to a separate method &lt;code&gt;TryGetNonEnumeratedCount()&lt;/code&gt;.
This method runs in constant time but may not always return a value.&lt;/p&gt;
&lt;p&gt;In .NET 9, the concept of using &lt;code&gt;IIListProvider&amp;lt;TSource&amp;gt;&lt;/code&gt; was further developed and the LINQ methods were refactored to use the new &lt;code&gt;Iterator&amp;lt;TSource&amp;gt;&lt;/code&gt; class, further improving performance.
As we can see, the obvious solution with checking the enumeration type and the &lt;code&gt;Count&lt;/code&gt; property has been implemented in new versions of .NET.&lt;/p&gt;
&lt;p&gt;Will this be effective for all collections?&lt;/p&gt;
&lt;h1 id="concurrentdictionary"&gt;ConcurrentDictionary&lt;/h1&gt;
&lt;p&gt;Let's look at the most common thread-safe collection - &lt;code&gt;ConcurrentDictionary&amp;lt;TKey, TValue&amp;gt;&lt;/code&gt;. It seems that there may be problems with the new implementation.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Benchmark code. Click to expand.&lt;/summary&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;using System.Collections.Concurrent;
using System.Linq;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;

namespace AnyVsCount
{
    [MemoryDiagnoser]
    [SimpleJob(RuntimeMoniker.Net90)]
    [SimpleJob(RuntimeMoniker.Net80)]
    [SimpleJob(RuntimeMoniker.Net60)]
    [SimpleJob(RuntimeMoniker.Net50)]
    [SimpleJob(RuntimeMoniker.NetCoreApp31)]
    [SimpleJob(RuntimeMoniker.Net48)]
    [HideColumns("Job", "Error", "StdDev", "Gen0")]
    public class ConcurrentDictionaryBenchmark
    {
        [Params(10, 10000)]
        public int N;

        private ConcurrentDictionary&amp;lt;int, string&amp;gt; dictionary;

        [GlobalSetup]
        public void SetUp()
        {
            dictionary = new ConcurrentDictionary&amp;lt;int, string&amp;gt;();

            for (int i = 0; i &amp;lt; N; i++)
            {
                dictionary[i] = i.ToString();
            }
        }

        [Benchmark]
        public bool DictionaryAny()
        {
            return dictionary.Any();
        }

        [Benchmark]
        public bool DictionaryCount()
        {
            return dictionary.Count != 0;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h3 id="results-1"&gt;Results&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Runtime&lt;/th&gt;
&lt;th&gt;N&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Mean&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Median&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Allocated&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryAny&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;18.48 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;18.47 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;64 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryCount&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;110.77 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;110.75 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryAny&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;18.21 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;18.17 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;64 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryCount&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;108.27 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;110.12 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryAny&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;92.98 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;92.81 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryCount&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;97.02 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;96.42 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryAny&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;87.96 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;87.79 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryCount&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;88.35 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;88.40 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryAny&lt;/td&gt;
&lt;td&gt;.NET 8.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;76.10 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;76.08 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryCount&lt;/td&gt;
&lt;td&gt;.NET 8.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;72.26 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;72.27 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryAny&lt;/td&gt;
&lt;td&gt;.NET 9.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;83.02 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;83.52 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryCount&lt;/td&gt;
&lt;td&gt;.NET 9.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;80.43 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;79.34 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryAny&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;18.87 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;18.71 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;64 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryCount&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6,870.38 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6,859.65 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryAny&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;18.04 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;18.06 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;64 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryCount&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;7,000.55 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;7,000.95 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryAny&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5,877.30 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5,877.50 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryCount&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5,962.47 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5,958.17 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryAny&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5,993.15 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5,994.17 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryCount&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6,030.51 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6,029.08 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryAny&lt;/td&gt;
&lt;td&gt;.NET 8.0&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5,082.70 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5,082.69 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryCount&lt;/td&gt;
&lt;td&gt;.NET 8.0&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5,370.34 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5,368.76 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryAny&lt;/td&gt;
&lt;td&gt;.NET 9.0&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5,917.83 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5,917.32 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryCount&lt;/td&gt;
&lt;td&gt;.NET 9.0&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5,847.50 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5,848.04 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Some features are already visible:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Execution time depends on the size of the collection.&lt;/li&gt;
&lt;li&gt;In older versions of the framework, the &lt;code&gt;Any()&lt;/code&gt; method was much faster.&lt;/li&gt;
&lt;li&gt;In newer versions of the framework, the &lt;code&gt;Any()&lt;/code&gt; method is on par with &lt;code&gt;Count&lt;/code&gt; in performance:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/any-part2/any2.png" alt="ConcurrentDictionary Any performance"&gt;&lt;/p&gt;
&lt;p&gt;Let's look at the implementation of the &lt;code&gt;Count&lt;/code&gt; property:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public int Count
{
    get
    {
        int locksAcquired = 0;
        try
        {
            AcquireAllLocks(ref locksAcquired);

            return GetCountNoLocks();
        }
        finally
        {
            ReleaseLocks(locksAcquired);
        }
    }
}

private void AcquireAllLocks(ref int locksAcquired)
{
    //...

    // First, acquire lock 0, then acquire the rest. _tables won't change after acquiring lock 0.
    AcquireFirstLock(ref locksAcquired);
    AcquirePostFirstLock(_tables, ref locksAcquired);
    Debug.Assert(locksAcquired == _tables._locks.Length);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Counting all elements requires acquiring locks on a special internal array &lt;code&gt;_tables._locks&lt;/code&gt;. Each element of this array locks a part of the dictionary.
Thus, starting with .NET 5, the &lt;code&gt;Any()&lt;/code&gt; method of &lt;code&gt;ConcurrentDictionary&lt;/code&gt; uses the &lt;code&gt;Count&lt;/code&gt; property through &lt;code&gt;ICollection&amp;lt;T&amp;gt;&lt;/code&gt;, which results in the same locks as calling &lt;code&gt;Count&lt;/code&gt; directly.&lt;/p&gt;
&lt;h3 id="any-isempty"&gt;Any() и IsEmpty&lt;/h3&gt;
&lt;p&gt;Let's reconstruct the implementation of &lt;code&gt;Any()&lt;/code&gt; with an iterator and check the performance of this method:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;[Benchmark]
public bool DictionaryEnumerator()
{
    using (var enumerator = dictionary.GetEnumerator())
    {
        return enumerator.MoveNext();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Runtime&lt;/th&gt;
&lt;th&gt;N&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Mean&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Median&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Allocated&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryEnumerator&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;15.51 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;15.52 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;64 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryEnumerator&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;14.60 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;14.60 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;64 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryEnumerator&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;15.16 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;15.14 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;64 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryEnumerator&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;18.60 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;18.67 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;64 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryEnumerator&lt;/td&gt;
&lt;td&gt;.NET 8.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;12.97 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;12.96 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;64 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryEnumerator&lt;/td&gt;
&lt;td&gt;.NET 9.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;12.80 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;12.81 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;64 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryEnumerator&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;15.41 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;15.38 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;64 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryEnumerator&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;15.03 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;14.96 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;64 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryEnumerator&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;15.90 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;15.92 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;64 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryEnumerator&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;18.50 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;18.50 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;64 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryEnumerator&lt;/td&gt;
&lt;td&gt;.NET 8.0&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;13.38 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;12.92 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;64 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryEnumerator&lt;/td&gt;
&lt;td&gt;.NET 9.0&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;12.97 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;12.53 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;64 B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;We see that the results do not depend on the number of elements and have remained virtually unchanged in newer versions of .NET.
But we spend a little memory creating the iterator each time.&lt;/p&gt;
&lt;p&gt;But you can use a specialized collection property - &lt;code&gt;IsEmpty&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;[Benchmark]
public bool DictionaryIsEmpty()
{
    return !dictionary.IsEmpty;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Runtime&lt;/th&gt;
&lt;th&gt;N&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Mean&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Median&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Allocated&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryIsEmpty&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;99.023 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;98.709 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryIsEmpty&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.313 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.313 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryIsEmpty&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.545 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.545 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryIsEmpty&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.247 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.283 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryIsEmpty&lt;/td&gt;
&lt;td&gt;.NET 8.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.567 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.568 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryIsEmpty&lt;/td&gt;
&lt;td&gt;.NET 9.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;10.917 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;10.902 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryIsEmpty&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6,027.991 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6,026.648 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryIsEmpty&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.320 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.315 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryIsEmpty&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.513 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.512 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryIsEmpty&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.756 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.759 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryIsEmpty&lt;/td&gt;
&lt;td&gt;.NET 8.0&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.673 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.673 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DictionaryIsEmpty&lt;/td&gt;
&lt;td&gt;.NET 9.0&lt;/td&gt;
&lt;td&gt;10000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.801 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.804 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;We see that starting with .NET Core 3.1, the implementation of this property does not depend on the size of the collection, and in the case of non-empty collections, calling this property is not blocking.
For .NET 4.8, we got results similar to calling the &lt;code&gt;Count&lt;/code&gt; property - a lock on the entire collection is also used.
For .NET 9, the results were slightly worse, but I don't have a quick answer as to why this happened.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;The implementation of the &lt;code&gt;Any()&lt;/code&gt; method is now optimized enough to be used in generic code, but there is still some room for its use in thread-safe collections.&lt;/p&gt;
&lt;h1 id="recommendations"&gt;Recommendations&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;Count&lt;/code&gt; properties for simple collections if you care about performance.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;Any()&lt;/code&gt; method for generic code and &lt;code&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;IsEmpty&lt;/code&gt; property if collections support it, starting with .NET Core 3.1.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>nvarchar(max) vs nvarchar(N)</title>
			<link>https://blog.rogatnev.net/posts/en/2025/04/nvarchar(max)-vs-nvarchar(N).html</link>
			<description>Understand the differences and performance implications of nvarchar(max) versus nvarchar(N) in Microsoft SQL Server. Practical examples show why choosing the right string data type matters for optimization.</description>
			<author>Sergey Rogatnev</author>
			<guid>https://blog.rogatnev.net/posts/en/2025/04/nvarchar(max)-vs-nvarchar(N).html</guid>
			<pubDate>Sat, 19 Apr 2025 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;MS SQL Server offers several data types for storing strings.
The most popular are &lt;code&gt;nvarchar(N)&lt;/code&gt; (1 ≤ N ≤ 4000) and &lt;code&gt;nvarchar(max)&lt;/code&gt;, which allow storing Unicode-encoded string data.
Application developers often transfer their experience with the &lt;code&gt;string&lt;/code&gt; type from programming languages like C# or Java to databases and automatically choose &lt;code&gt;nvarchar(max)&lt;/code&gt;, which can store strings up to 2GB.
However, databases store and process data in fundamentally different ways.
In this article, I will explain and demonstrate the consequences of unjustified use of the &lt;code&gt;nvarchar(max)&lt;/code&gt; type.&lt;/p&gt;
&lt;!--more--&gt;
&lt;h1 id="preparation"&gt;Preparation&lt;/h1&gt;
&lt;p&gt;Let’s create three tables to store text data:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE [dbo].[nv100]
(
	[Id] [int] NOT NULL,
	[CreateDate] [datetime] NOT NULL,
	[Comment] [nvarchar](100) NOT NULL,
	CONSTRAINT [PK_nv100_Id] PRIMARY KEY CLUSTERED ([Id] ASC)
)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE [dbo].[nv1000]
(
	[Id] [int] NOT NULL,
	[CreateDate] [datetime] NOT NULL,
	[Comment] [nvarchar](1000) NOT NULL,
	CONSTRAINT [PK_nv1000_Id] PRIMARY KEY CLUSTERED ([Id] ASC)
)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE [dbo].[nvmax]
(
	[Id] [int] NOT NULL,
	[CreateDate] [datetime] NOT NULL,
	[Comment] [nvarchar](max) NOT NULL,
	CONSTRAINT [PK_nvmax_Id] PRIMARY KEY CLUSTERED ([Id] ASC)
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The tables differ in the &lt;code&gt;Comment&lt;/code&gt; column type. Let’s populate them with 200,000 rows like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Id          CreateDate              Comment
----------- ----------------------- ----------
1           2025-01-01 00:00:01.000 1
2           2025-01-01 00:00:02.000 2
3           2025-01-01 00:00:03.000 3
4           2025-01-01 00:00:04.000 4
5           2025-01-01 00:00:05.000 5
6           2025-01-01 00:00:06.000 6
7           2025-01-01 00:00:07.000 7
8           2025-01-01 00:00:08.000 8
9           2025-01-01 00:00:09.000 9
10          2025-01-01 00:00:10.000 10
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id="testing-data-select"&gt;Testing Data Select&lt;/h1&gt;
&lt;p&gt;Let’s select all data from the tables and examine the execution plans:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;select *
from nv100

select *
from nv1000

select *
from nvmax
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At first glance, all three plans look identical:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/nvarchar/query1.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Let’s look at the single arrow in the first query’s plan:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/nvarchar/query1_1.png" alt="nvarchar(100)"&gt;&lt;/p&gt;
&lt;p&gt;SQL Server correctly estimated the number of rows to return: 200,000.
This is predictable since we’re selecting all data from the table.
The more interesting metric is &lt;code&gt;Estimated Row Size&lt;/code&gt; — the estimated size of one row in bytes.
How is this calculated? It includes row metadata and the actual data. For our case: 4 bytes for the &lt;code&gt;int&lt;/code&gt; identifier, 8 bytes for the &lt;code&gt;datetime&lt;/code&gt; field, and according to &lt;a href="https://learn.microsoft.com/en-us/sql/t-sql/data-types/nchar-and-nvarchar-transact-sql?view=sql-server-ver16"&gt;documentation&lt;/a&gt;, &lt;code&gt;nvarchar(n)&lt;/code&gt; requires 2*n bytes. For &lt;code&gt;nvarchar(100)&lt;/code&gt;, this is 200 bytes. Total: 4 + 8 + 200 = 212 bytes. Why does SQL Server estimate 123 bytes?&lt;/p&gt;
&lt;p&gt;When generating the query plan, SQL Server estimates &lt;code&gt;nvarchar(n)&lt;/code&gt; columns as half their maximum byte size. Thus, 100 bytes for the first table. The estimated row size becomes 4 + 8 + 100 = 112 bytes, plus metadata. Let’s check the second table’s plan:
&lt;img src="https://blog.rogatnev.net/img/nvarchar/query1_2.png" alt="nvarchar(1000)"&gt;&lt;/p&gt;
&lt;p&gt;Here, &lt;code&gt;Estimated Row Size&lt;/code&gt; increases to 1023 bytes, with 4 + 8 + 1000 = 1012 bytes for data. For &lt;code&gt;nvarchar(max)&lt;/code&gt;, which can store up to 2GB, does SQL Server estimate 1GB per row? No, it uses 4000 bytes as the default estimate:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/nvarchar/query1_3.png" alt="nvarchar(max)"&gt;&lt;/p&gt;
&lt;p&gt;If we exclude the &lt;code&gt;Comment&lt;/code&gt; column, SQL Server provides accurate estimates:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;select Id, CreateDate
from nvmax
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/nvarchar/query1_4.png" alt="nvarchar(max)"&gt;&lt;/p&gt;
&lt;h1 id="sorting"&gt;Sorting&lt;/h1&gt;
&lt;p&gt;When compiling a query plan, SQL Server considers factors like table size, indexes, data types, and &lt;code&gt;Estimated Row Size&lt;/code&gt; to determine memory allocation and whether to use &lt;code&gt;TempDB&lt;/code&gt; (e.g., for sorting large datasets).&lt;/p&gt;
&lt;p&gt;Let’s sort the data by Comment:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;select *
from nv100
order by Comment desc

select *
from nv1000
order by Comment desc

select *
from nvmax
order by Comment desc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Execution plans:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/nvarchar/query2.png" alt="order by Comment desc"&gt;&lt;/p&gt;
&lt;p&gt;The plans appear similar, but the second query shows a warning (&lt;code&gt;Excessive Grant&lt;/code&gt;) on the SELECT operator:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/nvarchar/query2_1.png" alt="nvarchar(1000)"&gt;&lt;/p&gt;
&lt;p&gt;SQL Server requested 295,616 KB (~300 MB) for execution but used only 14,584 KB (~15 MB) — 20 times less. The third query is worse:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/nvarchar/query2_2.png" alt="nvarchar(max)"&gt;&lt;/p&gt;
&lt;p&gt;It requested 1,122,496 KB (~1.1 GB) but used only 19,960 KB (~20 MB) — 56 times less. This unnecessary memory allocation can deprive other queries of resources.&lt;/p&gt;
&lt;h1 id="filtering"&gt;Filtering&lt;/h1&gt;
&lt;p&gt;Let’s filter by the &lt;code&gt;Comment&lt;/code&gt; column:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;select *
from nv100
where Comment = N'12345'

select *
from nv1000
where Comment = N'12345'

select *
from nvmax
where Comment = N'12345'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Execution plans:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/nvarchar/query3.png" alt="Filter by comment"&gt;&lt;/p&gt;
&lt;p&gt;All queries scan the clustered index, but the &lt;code&gt;nvmax&lt;/code&gt; table has an extra &lt;code&gt;Filter&lt;/code&gt; operation. Why? &lt;code&gt;nvarchar(max)&lt;/code&gt; data can be stored in two ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;IN_ROW_DATA&lt;/code&gt;: Stored directly in the row if ≤ 8000 bytes.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LOB_DATA&lt;/code&gt;: Stored in separate pages if &amp;gt; 8000 bytes, with a pointer in the row.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Check storage types:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT
	t.Name as TableName,
	a.type_desc as StorageType
FROM sys.tables t
JOIN sys.columns c ON t.object_id = c.object_id
JOIN sys.partitions p ON t.object_id = p.object_id
JOIN sys.allocation_units a ON p.partition_id = a.container_id
WHERE c.system_type_id = 231 -- nvarchar(max)
  AND a.type_desc IN ('IN_ROW_DATA', 'LOB_DATA')
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Result:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;TableName  StorageType
---------- -------------
nv100      IN_ROW_DATA
nv1000     IN_ROW_DATA
nvmax      IN_ROW_DATA
nvmax      LOB_DATA
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For &lt;code&gt;nvmax&lt;/code&gt;, data is stored in both formats, forcing SQL Server to retrieve data before applying filters, hence the extra &lt;code&gt;Filter&lt;/code&gt; step.&lt;/p&gt;
&lt;h1 id="indexes"&gt;Indexes&lt;/h1&gt;
&lt;p&gt;You cannot create an index on &lt;code&gt;nvarchar(max)&lt;/code&gt;. Attempting to index &lt;code&gt;nvarchar(1000)&lt;/code&gt; generates a warning:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE NONCLUSTERED INDEX [IX_nv1000_Comment] ON [dbo].[nv1000]
(
	[Comment] ASC
)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;Warning! The maximum key length for a nonclustered index is 1700 bytes. The index 'IX_nv1000_Comment' has maximum length of 2000 bytes. For some combination of large values, the insert/update operation will fail.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inserting a 1000-character string fails:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;insert into nv1000
values(-1, GETUTCDATE(), REPLICATE(N'A',1000))
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;Msg 1946, Level 16, State 3, Line 1
Operation failed. The index entry of length 2000 bytes for the index 'IX_nv1000_Comment' exceeds the maximum length of 1700 bytes for nonclustered indexes.
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id="comparison"&gt;Comparison&lt;/h1&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;nvarchar(100)&lt;/th&gt;
&lt;th&gt;nvarchar(1000)&lt;/th&gt;
&lt;th&gt;nvarchar(max)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Data Storage&lt;/td&gt;
&lt;td&gt;IN_ROW_DATA&lt;/td&gt;
&lt;td&gt;IN_ROW_DATA&lt;/td&gt;
&lt;td&gt;IN_ROW_DATA/LOB_DATA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Estimated Row Size&lt;/td&gt;
&lt;td&gt;100 bytes&lt;/td&gt;
&lt;td&gt;1000 bytes&lt;/td&gt;
&lt;td&gt;4000 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Index Support&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (with restrictions)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h1 id="conclusions"&gt;Conclusions&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;n&lt;/code&gt; in &lt;code&gt;nvarchar(n)&lt;/code&gt; is not just a maximum length — it helps SQL Server optimize query plans.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;nvarchar(max)&lt;/code&gt; only for genuinely large strings. Apply business logic constraints (e.g., user input limits) to the database.&lt;/li&gt;
&lt;li&gt;The optimal &lt;code&gt;nvarchar(n)&lt;/code&gt; size is twice the average stored string length.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="references"&gt;References&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/ru-ru/sql/t-sql/data-types/nchar-and-nvarchar-transact-sql?view=sql-server-ver16"&gt;nchar и nvarchar (Transact-SQL)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>List initializer</title>
			<link>https://blog.rogatnev.net/posts/en/2024/01/List-initializer.html</link>
			<description>Explore the inner workings of List&lt;T&gt; initialization in C#. Compare arrays and lists, discover their impact on performance and memory allocation, and learn optimization techniques for high-performance code.</description>
			<author>Sergey Rogatnev</author>
			<guid>https://blog.rogatnev.net/posts/en/2024/01/List-initializer.html</guid>
			<pubDate>Sat, 06 Jan 2024 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;We all used to type &lt;code&gt;new List&amp;lt;int&amp;gt; { 1, 2, 3, 4 }&lt;/code&gt; or &lt;code&gt;new int[] { 1, 2, 3, 4}&lt;/code&gt; to initialize collections with some values. It looks similar in syntax but differs in behavior, and you should be careful if you are concerned about performance.&lt;/p&gt;
&lt;!--more--&gt;
&lt;h1 id="array"&gt;Array&lt;/h1&gt;
&lt;p&gt;As we know, &lt;a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/arrays"&gt;arrays&lt;/a&gt; contains a sequence of elements with a fixed size. Once it was created it can't be changed during the lifetime of the instance.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var array = new int[] { 1, 2, 3, 4};
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id="listt"&gt;List&amp;lt;T&amp;gt;&lt;/h1&gt;
&lt;p&gt;When we don't know final size of a collection, or we need to add/delete elements during the lifetime, it's suitable to use the &lt;a href="https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1?view=net-8.0"&gt;&lt;code&gt;List&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/a&gt; type.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var list = new List&amp;lt;int&amp;gt;();
list.Add(1);
list.Add(2);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At first glance it may seem that we can always use the list instead of the array — it has all array's features, but also can be dynamically changed. But to decide whether to use the list, we need to know more about its internal structure.&lt;/p&gt;
&lt;p&gt;Part of the source code:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public class List&amp;lt;T&amp;gt; : IList&amp;lt;T&amp;gt;, IList, IReadOnlyList&amp;lt;T&amp;gt;
{
    private const int DefaultCapacity = 4;

    internal T[] _items;
    internal int _size;

    private static readonly T[] s_emptyArray = new T[0];

    // Constructs a List. The list is initially empty and has a capacity
    // of zero. Upon adding the first element to the list the capacity is
    // increased to DefaultCapacity, and then increased in multiples of two
    // as required.
    public List()
    {
        _items = s_emptyArray;
    }
    ...

    public int Capacity
    {
        get =&amp;gt; _items.Length;
        set {...}
    }

    public int Count =&amp;gt; _size;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;_items&lt;/code&gt; - array with stored elements;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;_size&lt;/code&gt; - count of elements stored in the list and size of the whole list;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Capacity&lt;/code&gt; - size of &lt;code&gt;_items&lt;/code&gt; and maximum number of elements can be stored without a resize.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="list-resize"&gt;List resize&lt;/h2&gt;
&lt;p&gt;To put it simply, we can say that the list is just the array that can resize when needed.
Every time we try to add one more item the list ensures &lt;code&gt;_items&lt;/code&gt; has enough free space, otherwise it sets new capacity:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;internal void Grow(int capacity)
{
    ...
    int newCapacity = _items.Length == 0 ? DefaultCapacity : 2 * _items.Length;
    ...
    Capacity = newCapacity;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's look closer to the &lt;code&gt;Capacity&lt;/code&gt; property:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;// Gets and sets the capacity of this list.  The capacity is the size of
// the internal array used to hold items.  When set, the internal
// array of the list is reallocated to the given capacity.
public int Capacity
{
    get =&amp;gt; _items.Length;
    set
    {
        if (value &amp;lt; _size)
        {
            ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_SmallCapacity);
        }

        if (value != _items.Length)
        {
            if (value &amp;gt; 0)
            {
                T[] newItems = new T[value];
                if (_size &amp;gt; 0)
                {
                    Array.Copy(_items, newItems, _size);
                }
                _items = newItems;
            }
            else
            {
                _items = s_emptyArray;
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, what do we see? Initially, every list is created with an empty internal array. After adding the first element the list creates the new array for 4 elements (&lt;code&gt;DefaultCapacity&lt;/code&gt; is 4). And when the current array is exhausted, the new one is created with doubled size, and all elements are copied.&lt;/p&gt;
&lt;h1 id="performance"&gt;Performance&lt;/h1&gt;
&lt;p&gt;What happens when we create a new list and initialize it with some values?&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var list = new List&amp;lt;int&amp;gt; { 1, 2, 3, 4, 5 };
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It looks like array initialization but works very different way. According to the &lt;a href="https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers#collection-initializers"&gt;documentation&lt;/a&gt;, any type that implements &lt;code&gt;IEnumerable&lt;/code&gt; and has &lt;code&gt;Add&lt;/code&gt; method can be used with collection initializer.&lt;/p&gt;
&lt;p&gt;So the previous example is just short form of serial calls of &lt;code&gt;Add&lt;/code&gt; method:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var list = new List&amp;lt;int&amp;gt;();
list.Add(1);
list.Add(2);
list.Add(3);
list.Add(4);
list.Add(5);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The compiler just transforms short collection initializer and adds the necessary calls automatically. And this may cause performance degradation.&lt;/p&gt;
&lt;p&gt;Let's take a closer look at the process of adding 5 elements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Before the first &lt;code&gt;Add&lt;/code&gt; call the list is empty, the internal array is empty.&lt;/li&gt;
&lt;li&gt;The first added element creates the new internal array for 4 elements.&lt;/li&gt;
&lt;li&gt;Elements 2, 3, 4 change nothing.&lt;/li&gt;
&lt;li&gt;When we add the fifth element, the internal array is full and needs to be resized. The new array of size 8 is created, all elements are copied from the previous array, and the fifth element is added.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At the end we have the list with 5 elements and an internal array for 8 elements. At the same time, we created 2 arrays, and the final one wastes 37.5% of its space for nothing.&lt;/p&gt;
&lt;p&gt;As you might guess, creating new arrays and copying elements causes memory allocations and takes additional time.&lt;/p&gt;
&lt;p&gt;This could be an unpleasant surprise for critical places. Do we have a solution? Yes!&lt;/p&gt;
&lt;h2 id="capacity"&gt;Capacity&lt;/h2&gt;
&lt;p&gt;If we know or suggest the final size of the list, we can create it with initial capacity.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var list = new List&amp;lt;int&amp;gt;(5);
list.Add(1);
list.Add(2);
list.Add(3);
list.Add(4);
list.Add(5);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var list = new List&amp;lt;int&amp;gt;(5) { 1, 2, 3, 4, 5 };
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we immediately create one array for 5 elements and no more unnecessary allocations.&lt;/p&gt;
&lt;h2 id="benchmarks"&gt;Benchmarks&lt;/h2&gt;
&lt;p&gt;Let's compare creating lists with initial capacity and without it.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Benchmark code. Click to expand.&lt;/summary&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;

namespace Benchmarks
{
    [ShortRunJob(RuntimeMoniker.Net48)]
    [ShortRunJob(RuntimeMoniker.Net60)]
    [ShortRunJob(RuntimeMoniker.Net80)]
    [MemoryDiagnoser]
    public class InitListBenchmark
    {
        [BenchmarkCategory(&amp;quot;One&amp;quot;)]
        [Benchmark]
        public List&amp;lt;int&amp;gt; InitList1()
        {
            return new List&amp;lt;int&amp;gt; {1};
        }

        [BenchmarkCategory(&amp;quot;One&amp;quot;)]
        [Benchmark]
        public List&amp;lt;int&amp;gt; InitListWithSize1()
        {
            return new List&amp;lt;int&amp;gt;(1) {1};
        }

        [BenchmarkCategory(&amp;quot;Five&amp;quot;)]
        [Benchmark]
        public List&amp;lt;int&amp;gt; InitList5()
        {
            return new List&amp;lt;int&amp;gt; {1, 2, 3, 4, 5};
        }
        
        [BenchmarkCategory(&amp;quot;Five&amp;quot;)]
        [Benchmark]
        public List&amp;lt;int&amp;gt; InitListWithSize5()
        {
            return new List&amp;lt;int&amp;gt;(5) {1, 2, 3, 4, 5};
        }
        
        [BenchmarkCategory(&amp;quot;Ten&amp;quot;)]
        [Benchmark]
        public List&amp;lt;int&amp;gt; InitList10()
        {
            return new List&amp;lt;int&amp;gt; {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
        }
        
        [BenchmarkCategory(&amp;quot;Ten&amp;quot;)]
        [Benchmark]
        public List&amp;lt;int&amp;gt; InitListWithSize10()
        {
            return new List&amp;lt;int&amp;gt;(10) {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;pre&gt;&lt;code&gt;
BenchmarkDotNet v0.13.8, Windows 10 (10.0.19045.3930/22H2/2022Update)
AMD Ryzen 7 7840H with Radeon 780M Graphics, 1 CPU, 16 logical and 8 physical cores
.NET SDK 8.0.101
  [Host]                      : .NET 6.0.26 (6.0.2623.60508), X64 RyuJIT AVX2
  ShortRun-.NET 6.0           : .NET 6.0.26 (6.0.2623.60508), X64 RyuJIT AVX2
  ShortRun-.NET 8.0           : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX2
  ShortRun-.NET Framework 4.8 : .NET Framework 4.8.1 (4.8.9195.0), X64 RyuJIT VectorSize=256

IterationCount=3  LaunchCount=1  WarmupCount=3  

&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Runtime&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Mean&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Gen0&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Allocated&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;InitList1&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td style="text-align: right;"&gt;14.523 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0127&lt;/td&gt;
&lt;td style="text-align: right;"&gt;80 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;InitListWithSize1&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6.678 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0115&lt;/td&gt;
&lt;td style="text-align: right;"&gt;72 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;InitList5&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td style="text-align: right;"&gt;33.884 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0216&lt;/td&gt;
&lt;td style="text-align: right;"&gt;136 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;InitListWithSize5&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td style="text-align: right;"&gt;12.232 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0140&lt;/td&gt;
&lt;td style="text-align: right;"&gt;88 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;InitList10&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td style="text-align: right;"&gt;54.156 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0357&lt;/td&gt;
&lt;td style="text-align: right;"&gt;225 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;InitListWithSize10&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td style="text-align: right;"&gt;18.609 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0166&lt;/td&gt;
&lt;td style="text-align: right;"&gt;104 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;InitList1&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;10.599 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0086&lt;/td&gt;
&lt;td style="text-align: right;"&gt;72 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;InitListWithSize1&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5.727 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0076&lt;/td&gt;
&lt;td style="text-align: right;"&gt;64 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;InitList5&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;22.426 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0153&lt;/td&gt;
&lt;td style="text-align: right;"&gt;128 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;InitListWithSize5&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;8.611 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0096&lt;/td&gt;
&lt;td style="text-align: right;"&gt;80 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;InitList10&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;37.512 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0258&lt;/td&gt;
&lt;td style="text-align: right;"&gt;216 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;InitListWithSize10&lt;/td&gt;
&lt;td&gt;.NET 6.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;12.740 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0115&lt;/td&gt;
&lt;td style="text-align: right;"&gt;96 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;InitList1&lt;/td&gt;
&lt;td&gt;.NET 8.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;9.515 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0086&lt;/td&gt;
&lt;td style="text-align: right;"&gt;72 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;InitListWithSize1&lt;/td&gt;
&lt;td&gt;.NET 8.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6.638 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0076&lt;/td&gt;
&lt;td style="text-align: right;"&gt;64 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;InitList5&lt;/td&gt;
&lt;td&gt;.NET 8.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;23.910 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0153&lt;/td&gt;
&lt;td style="text-align: right;"&gt;128 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;InitListWithSize5&lt;/td&gt;
&lt;td&gt;.NET 8.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;9.153 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0096&lt;/td&gt;
&lt;td style="text-align: right;"&gt;80 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;InitList10&lt;/td&gt;
&lt;td&gt;.NET 8.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;38.765 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0258&lt;/td&gt;
&lt;td style="text-align: right;"&gt;216 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;InitListWithSize10&lt;/td&gt;
&lt;td&gt;.NET 8.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;12.288 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0115&lt;/td&gt;
&lt;td style="text-align: right;"&gt;96 B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;When we set initial capacity, it doesn't lead to unnecessary allocations, and we see better performance. And there is no excess memory traffic. The more elements we add to the list, the more difference we see in benchmarks.&lt;/p&gt;
&lt;h2 id="analyzer"&gt;Analyzer&lt;/h2&gt;
&lt;p&gt;If performance is critical to your project, you should pay attention to these situations. Automatic diagnostics can help you.
I support a set of roslyn-based diagnostics - &lt;a href="https://www.nuget.org/packages/Collections.Analyzer"&gt;Collections.Analyzer&lt;/a&gt; and now it can detect lists with collection initializer and without initial capacity.&lt;/p&gt;
&lt;h1 id="opinion"&gt;Opinion&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;If you need a static collection, you won't change (add or remove elements) — use arrays.&lt;/li&gt;
&lt;li&gt;If you create a list, and you know exactly its future size — set initial capacity with this size.&lt;/li&gt;
&lt;li&gt;If you create a list, and you don't know its future size — set the expected size.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="links"&gt;Links&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers#collection-initializers"&gt;Collection initializers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Backs/Collections.Analyzer"&gt;Collections.Analyzer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>Harmful collection transformations. Part 3: collections</title>
			<link>https://blog.rogatnev.net/posts/en/2022/01/Harmful-collection-transformations-part-3-collections.html</link>
			<description>Learn how unnecessary conversion to arrays and misuse of collection transformations in .NET can harm performance. Includes benchmarks, code analysis, and guidance on writing more efficient LINQ queries.</description>
			<author>Sergey Rogatnev</author>
			<guid>https://blog.rogatnev.net/posts/en/2022/01/Harmful-collection-transformations-part-3-collections.html</guid>
			<pubDate>Sat, 08 Jan 2022 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;Starting with &lt;code&gt;string&lt;/code&gt; in &lt;a href="xref:2021-09-23-Harmful-collection-transformations-part-1-strings"&gt;the first post&lt;/a&gt; we continue to study examples with collection transformations and how they affect our applications.&lt;/p&gt;
&lt;!--more--&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="xref:2021-09-23-Harmful-collection-transformations-part-1-strings"&gt;Part 1: string to array of chars&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="xref:2021-10-13-Harmful-collection-transformations-part-2-diagnostics"&gt;Part 2: automatic diagnostics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 3: collections&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="collections"&gt;Collections&lt;/h1&gt;
&lt;p&gt;.NET contains hundreds of different collections for any purpose, but it looks like developers used to use &lt;code&gt;List&amp;lt;T&amp;gt;&lt;/code&gt; or &lt;code&gt;Array&lt;/code&gt; so much that they convert anything into an array just in case. In most cases, this is unnecessary. Sometimes it's harmful as we found out earlier. Let's look at some examples:&lt;/p&gt;
&lt;h2 id="count-elements"&gt;Count elements&lt;/h2&gt;
&lt;p&gt;Sometimes we don't need all elements but only count them. And we can see the code like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;return certificates.Where(x =&amp;gt; certificateStatesByFormId.SafeGet(x.Id) == Form.CertificateState.Revoked || x.ValidToTicks &amp;lt;= nowTicks - 1)
   .ToArray().Length;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or another example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public int GetTotalAmount(Guid managerId)
{
   return filterSettingsHandler
       .Select&amp;lt;ClientFilterDbo&amp;gt;(managerId)
       .Where(IsVisible)
       .ToArray().Length;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Creating an array to count elements is unnecessary. LINQ provides a &lt;code&gt;Count()&lt;/code&gt; method that does what we need without harmful overhead. And we can easily rewrite examples:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;return certificates.Count(x =&amp;gt; certificateStatesByFormId.SafeGet(x.Id) == Form.CertificateState.Revoked || x.ValidToTicks &amp;lt;= nowTicks - 1);
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public int GetTotalAmount(Guid managerId)
{
   return filterSettingsHandler
       .Select&amp;lt;ClientFilterDbo&amp;gt;(managerId)
       .Count(IsVisible);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We get the cleaner and more readable code. Besides that, we got some performance improvements.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Benchmark code. Click to expand.&lt;/summary&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;namespace ToCharArrayBenchmark
{
    using System.Linq;
    using BenchmarkDotNet.Attributes;
    using BenchmarkDotNet.Jobs;

    [HtmlExporter]
    [RPlotExporter]
    [SimpleJob(RuntimeMoniker.Net48, baseline: true)]
    [SimpleJob(RuntimeMoniker.NetCoreApp31)]
    [SimpleJob(RuntimeMoniker.Net50)]
    [MemoryDiagnoser]
    public class CollectionsCountBenchmark
    {
        [Params(10, 20, 50, 100, 500)]
        public int N { get; set; }
        
        private string[] array;
        
        [GlobalSetup]
        public void SetUp()
        {
            array = new string[N];
            for (int i = 0; i &amp;lt; N; i++)
            {
                array[i] = i.ToString();
            }
        }

        [Benchmark(Baseline = true)]
        public int Count()
        {
            return array.Where(o =&amp;gt; o != "1").Count();
        }
        
        [Benchmark]
        public int ArrayLength()
        {
            return array.Where(o =&amp;gt; o != "1").ToArray().Length;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;Full results in table format. Click to expand.&lt;/summary&gt;
&lt;pre&gt;&lt;code class="language-ini"&gt;
BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19043.1348 (21H1/May2021Update)
AMD Ryzen 7 4700U with Radeon Graphics, 1 CPU, 8 logical and 8 physical cores
  [Host]             : .NET Framework 4.8 (4.8.4420.0), X64 RyuJIT
  .NET 5.0           : .NET 5.0.7 (5.0.721.25508), X64 RyuJIT
  .NET Core 3.1      : .NET Core 3.1.20 (CoreCLR 4.700.21.47003, CoreFX 4.700.21.47101), X64 RyuJIT
  .NET Framework 4.8 : .NET Framework 4.8 (4.8.4420.0), X64 RyuJIT


&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Runtime&lt;/th&gt;
&lt;th&gt;N&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Mean&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Error&lt;/th&gt;
&lt;th style="text-align: right;"&gt;StdDev&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Ratio&lt;/th&gt;
&lt;th style="text-align: right;"&gt;RatioSD&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Gen 0&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Allocated&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Count&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;.NET 5.0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;106.4 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;1.78 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;1.67 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.70&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.02&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.0229&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;48 B&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayLength&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;263.5 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.93 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.28 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.72&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.1798&lt;/td&gt;
&lt;td style="text-align: right;"&gt;376 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Count&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;108.2 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.66 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.39 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.71&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0229&lt;/td&gt;
&lt;td style="text-align: right;"&gt;48 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayLength&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;286.0 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.52 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.12 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.88&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.1798&lt;/td&gt;
&lt;td style="text-align: right;"&gt;376 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Count&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;151.7 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.52 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.90 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0267&lt;/td&gt;
&lt;td style="text-align: right;"&gt;56 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayLength&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;361.4 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6.58 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;9.65 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.39&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.2141&lt;/td&gt;
&lt;td style="text-align: right;"&gt;449 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Count&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;.NET 5.0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;139.5 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.79 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.74 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.61&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.01&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.0229&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;48 B&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayLength&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;378.4 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;7.24 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6.77 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.67&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.2713&lt;/td&gt;
&lt;td style="text-align: right;"&gt;568 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Count&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;136.5 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.63 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.59 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.60&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0229&lt;/td&gt;
&lt;td style="text-align: right;"&gt;48 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayLength&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;419.6 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.89 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.57 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.85&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.2713&lt;/td&gt;
&lt;td style="text-align: right;"&gt;568 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Count&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;227.0 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.01 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.82 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0267&lt;/td&gt;
&lt;td style="text-align: right;"&gt;56 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayLength&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;595.2 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6.18 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5.48 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.63&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.3862&lt;/td&gt;
&lt;td style="text-align: right;"&gt;810 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Count&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;.NET 5.0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;50&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;253.4 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.99 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.83 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.59&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.00&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.0229&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;48 B&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayLength&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;708.7 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;8.07 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;7.55 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.66&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.5121&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,072 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Count&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;248.0 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.19 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.05 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.58&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0229&lt;/td&gt;
&lt;td style="text-align: right;"&gt;48 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayLength&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;763.3 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;12.10 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;10.72 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.79&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.5121&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,072 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Count&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;426.5 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.19 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.94 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0267&lt;/td&gt;
&lt;td style="text-align: right;"&gt;56 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayLength&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,334.4 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;26.38 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;25.91 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.12&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.06&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.7572&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,589 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Count&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;.NET 5.0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;458.0 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;2.64 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;2.34 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.60&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.00&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.0229&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;48 B&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayLength&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,185.5 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;11.97 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;11.20 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.55&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.9060&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,896 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Count&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;414.5 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.15 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.96 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.54&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0229&lt;/td&gt;
&lt;td style="text-align: right;"&gt;48 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayLength&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,236.0 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;23.07 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;22.65 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.62&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.9060&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,896 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Count&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;765.6 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.88 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.32 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0267&lt;/td&gt;
&lt;td style="text-align: right;"&gt;56 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayLength&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,521.6 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;48.92 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;61.87 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.30&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.09&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.4496&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3,041 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Count&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;.NET 5.0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;500&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;2,010.3 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;7.45 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;6.97 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.61&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.02&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.0229&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;48 B&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayLength&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4,572.5 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;86.28 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;80.71 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.38&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.05&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.0283&lt;/td&gt;
&lt;td style="text-align: right;"&gt;8,432 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Count&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,893.9 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;9.48 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;8.40 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.57&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0229&lt;/td&gt;
&lt;td style="text-align: right;"&gt;48 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayLength&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4,395.2 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;86.59 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;88.92 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.33&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.05&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.0283&lt;/td&gt;
&lt;td style="text-align: right;"&gt;8,432 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Count&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3,298.3 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;64.73 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;79.49 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0267&lt;/td&gt;
&lt;td style="text-align: right;"&gt;56 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ArrayLength&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td style="text-align: right;"&gt;11,853.8 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;235.81 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;280.71 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.59&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.12&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5.9204&lt;/td&gt;
&lt;td style="text-align: right;"&gt;12,470 B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre&gt;&lt;code&gt;// * Legends *
  N         : Value of the 'N' parameter
  Mean      : Arithmetic mean of all measurements
  Error     : Half of 99.9% confidence interval
  StdDev    : Standard deviation of all measurements
  Ratio     : Mean of the ratio distribution ([Current]/[Baseline])
  RatioSD   : Standard deviation of the ratio distribution ([Current]/[Baseline])
  Gen 0     : GC Generation 0 collects per 1000 operations
  Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)
  1 us      : 1 Microsecond (0.000001 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;p&gt;The following diagram shows execution time of both methods (in microseconds) for .NET 5 platform:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/collections/count-diagram-time.png" alt="Time diagram"&gt;&lt;/p&gt;
&lt;p&gt;As we see, converting to array is about 2.5 times slower even for the most optimized .NET 5. Other frameworks are up to 4 times slower.&lt;/p&gt;
&lt;p&gt;And if this is not enough, let's look at memory allocations (in bytes):&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/collections/count-diagram-memory.png" alt="Allocated memory diagram"&gt;&lt;/p&gt;
&lt;p&gt;The LINQ method creates few objects and does not lead to additional GC regardless of the size of the original collection.&lt;/p&gt;
&lt;p&gt;We can confidently say that creating an array to count elements is a harmful transformation.&lt;/p&gt;
&lt;h2 id="toarray-with-linq"&gt;&lt;code&gt;ToArray()&lt;/code&gt; with LINQ&lt;/h2&gt;
&lt;p&gt;The widespread pattern - to call &lt;code&gt;ToArray()&lt;/code&gt; (just in case) before LINQ-methods.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var correctPaymentsByBillId = correctPayments
   .GroupBy(x =&amp;gt; x.BillId)
   .ToDictionary(x =&amp;gt; x.Key, x =&amp;gt; x.ToArray().Last());
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here &lt;code&gt;GroupBy&lt;/code&gt; returns &lt;code&gt;IGrouping&amp;lt;out TKey, out TElement&amp;gt;&lt;/code&gt; interface and it, in turns, implements &lt;code&gt;IEnumerable&amp;lt;TElement&amp;gt;&lt;/code&gt;. Based on this we can simply remove this &lt;code&gt;ToArray()&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var correctPaymentsByBillId = correctPayments
   .GroupBy(x =&amp;gt; x.BillId)
   .ToDictionary(x =&amp;gt; x.Key, x =&amp;gt; x.Last());
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Array&lt;/code&gt; is often used as a return type of methods like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;private OrderPackage[] ReadRootPackages(params Guid[] packageIds)
{
    var rootPackages = new Dictionary&amp;lt;Guid, OrderPackage&amp;gt;();
    //...
    return rootPackages.Values.ToArray();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the result doesn't have to be an array or any specific collection:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public OrderPackageDto[] SelectTariffForestByPackageIds(Guid[] packageIds)
{
	var rootOrderPackages = ReadRootPackages(packageIds);
	var masterPackageIds = rootOrderPackages.Select(package =&amp;gt; package.Id).ToArray();
	...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As we see, &lt;code&gt;ReadRootPackages&lt;/code&gt; can return &lt;code&gt;ICollection&amp;lt;OrderPackage&amp;gt;&lt;/code&gt; or &lt;code&gt;IEnumerable&amp;lt;OrderPackage&amp;gt;&lt;/code&gt; and &lt;code&gt;ToArray()&lt;/code&gt; is redundant:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;private IEnumerable&amp;lt;OrderPackage&amp;gt; ReadRootPackages(params Guid[] packageIds)
{
    var rootPackages = new Dictionary&amp;lt;Guid, OrderPackage&amp;gt;();
    //...
    return rootPackages.Values;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The most obvious example is declaring method return type as &lt;code&gt;IEnumerable&lt;/code&gt; but returning an array:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public IEnumerable&amp;lt;Division&amp;gt; GetDivisions(HashSet&amp;lt;SupportUserAccessRight&amp;gt; accessRights)
{
   return divisions.Where(d =&amp;gt; d.AccessRights.Intersect(accessRights).Any()).OrderBy(d =&amp;gt; d.Priority).ToArray();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Like the previous example &lt;code&gt;GetDivisions&lt;/code&gt; is used in LINQ-methods chain, and we can remove &lt;code&gt;ToArray()&lt;/code&gt; call:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public IEnumerable&amp;lt;Division&amp;gt; GetDivisions(HashSet&amp;lt;SupportUserAccessRight&amp;gt; accessRights)
{
   return divisions.Where(d =&amp;gt; d.AccessRights.Intersect(accessRights).Any()).OrderBy(d =&amp;gt; d.Priority);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Usually, you don't need a collection between LINQ-methods, because of the lazy evaluation nature of LINQ.
How does &lt;code&gt;ToArray()&lt;/code&gt; affect performance depending on different collections? Let's compare two versions - with &lt;code&gt;ToArray()&lt;/code&gt; and without it.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Benchmark code. Click to expand.&lt;/summary&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;namespace ToCharArrayBenchmark
{
    using System.Collections.Generic;
    using System.Linq;
    using BenchmarkDotNet.Attributes;
    using BenchmarkDotNet.Engines;
    using BenchmarkDotNet.Jobs;

    [HtmlExporter]
    [RPlotExporter]
    [SimpleJob(RuntimeMoniker.Net48, baseline: true)]
    [SimpleJob(RuntimeMoniker.NetCoreApp31)]
    [SimpleJob(RuntimeMoniker.Net50)]
    [MemoryDiagnoser]
    public class CollectionToArrayBenchmark
    {
        [Params(10, 20, 50, 100, 500)]
        public int N { get; set; }

        private List&amp;lt;string&amp;gt; list;
        private string[] array;
        private HashSet&amp;lt;string&amp;gt; hashSet;
        private Dictionary&amp;lt;long, string&amp;gt; dictionary;
        private readonly Consumer consumer = new Consumer();

        [GlobalSetup]
        public void SetUp()
        {
            array = new string[N];
            list = new List&amp;lt;string&amp;gt;(N);
            dictionary = new Dictionary&amp;lt;long, string&amp;gt;(N);
            hashSet = new HashSet&amp;lt;string&amp;gt;(N);

            for (int i = 0; i &amp;lt; N; i++)
            {
                list.Add(i.ToString());
                array[i] = i.ToString();
                hashSet.Add(i.ToString());
                dictionary[i] = i.ToString();
            }
        }

        [Benchmark(Baseline = true)]
        public void FilterArray()
        {
            GetFilteredEnumerable(array).Consume(consumer);
        }

        [Benchmark]
        public void FilterList()
        {
            GetFilteredEnumerable(list).Consume(consumer);
        }

        [Benchmark]
        public void FilterHashSet()
        {
            GetFilteredEnumerable(hashSet).Consume(consumer);
        }

        [Benchmark]
        public void FilterDictionary()
        {
            GetFilteredEnumerable(dictionary.Values).Consume(consumer);
        }

        [Benchmark]
        public void FilterArray_ToArray()
        {
            GetFilteredArray(array).Consume(consumer);
        }

        [Benchmark]
        public void FilterList_ToArray()
        {
            GetFilteredArray(list).Consume(consumer);
        }

        [Benchmark]
        public void FilterHashSet_ToArray()
        {
            GetFilteredArray(hashSet).Consume(consumer);
        }

        [Benchmark]
        public void FilterDictionary_ToArray()
        {
            GetFilteredArray(dictionary.Values).Consume(consumer);
        }

        private static IEnumerable&amp;lt;string&amp;gt; GetFilteredEnumerable(IEnumerable&amp;lt;string&amp;gt; strings)
        {
            return strings.Where(o =&amp;gt; o.Length &amp;gt; 1).Select(o =&amp;gt; o);
        }

        private static string[] GetFilteredArray(IEnumerable&amp;lt;string&amp;gt; strings)
        {
            return strings.Where(o =&amp;gt; o.Length &amp;gt; 1).ToArray();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;Full results in table format. Click to expand.&lt;/summary&gt;
&lt;pre&gt;&lt;code&gt;BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19043.1288 (21H1/May2021Update)
AMD Ryzen 7 4700U with Radeon Graphics, 1 CPU, 8 logical and 8 physical cores
  [Host]             : .NET Framework 4.8 (4.8.4420.0), X64 RyuJIT
  .NET 5.0           : .NET 5.0.7 (5.0.721.25508), X64 RyuJIT
  .NET Core 3.1      : .NET Core 3.1.20 (CoreCLR 4.700.21.47003, CoreFX 4.700.21.47101), X64 RyuJIT
  .NET Framework 4.8 : .NET Framework 4.8 (4.8.4420.0), X64 RyuJIT
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Runtime&lt;/th&gt;
&lt;th&gt;N&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Mean&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Error&lt;/th&gt;
&lt;th style="text-align: right;"&gt;StdDev&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Median&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Ratio&lt;/th&gt;
&lt;th style="text-align: right;"&gt;RatioSD&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Gen 0&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Allocated&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;FilterArray&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;.NET 5.0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;88.86 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.937 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.876 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;88.73 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.80&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.01&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.0497&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;104 B&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;156.24 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.318 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.169 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;155.86 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.40&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0725&lt;/td&gt;
&lt;td style="text-align: right;"&gt;152 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;212.50 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.097 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.746 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;211.43 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.91&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;160 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;209.59 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.441 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.283 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;209.03 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.89&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;160 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterArray_ToArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;88.34 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.332 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.180 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;88.73 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.79&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0229&lt;/td&gt;
&lt;td style="text-align: right;"&gt;48 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList_ToArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;100.15 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.151 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.961 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;100.02 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.90&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0343&lt;/td&gt;
&lt;td style="text-align: right;"&gt;72 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet_ToArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;197.00 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.438 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.345 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;196.58 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.77&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0458&lt;/td&gt;
&lt;td style="text-align: right;"&gt;96 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary_ToArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;206.34 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.901 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.649 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;204.96 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.86&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0458&lt;/td&gt;
&lt;td style="text-align: right;"&gt;96 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;100.98 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.096 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.972 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;100.91 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.91&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0497&lt;/td&gt;
&lt;td style="text-align: right;"&gt;104 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;166.74 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.152 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.021 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;166.57 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0725&lt;/td&gt;
&lt;td style="text-align: right;"&gt;152 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;240.48 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.734 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;7.644 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;236.99 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.16&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.09&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;160 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;210.68 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.404 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.173 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;210.80 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.89&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;160 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterArray_ToArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;94.07 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.784 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.615 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;93.07 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.84&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0229&lt;/td&gt;
&lt;td style="text-align: right;"&gt;48 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList_ToArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;97.77 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.811 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.606 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;97.25 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.88&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0343&lt;/td&gt;
&lt;td style="text-align: right;"&gt;72 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet_ToArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;210.94 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.940 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5.650 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;210.06 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.92&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.06&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0458&lt;/td&gt;
&lt;td style="text-align: right;"&gt;96 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary_ToArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;204.44 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.835 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.587 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;203.66 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.83&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0458&lt;/td&gt;
&lt;td style="text-align: right;"&gt;96 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;111.34 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.837 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.534 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;111.21 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0573&lt;/td&gt;
&lt;td style="text-align: right;"&gt;120 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;161.72 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.305 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.156 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;161.68 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.45&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0725&lt;/td&gt;
&lt;td style="text-align: right;"&gt;152 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;213.74 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.929 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.675 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;213.37 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.92&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.05&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;160 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;212.36 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.331 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.116 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;211.33 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.91&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;160 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterArray_ToArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;125.26 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.469 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.310 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;124.86 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.12&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0381&lt;/td&gt;
&lt;td style="text-align: right;"&gt;80 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList_ToArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;182.54 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.822 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.640 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;182.31 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.64&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0458&lt;/td&gt;
&lt;td style="text-align: right;"&gt;96 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet_ToArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;223.26 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.104 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.638 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;223.15 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.01&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.05&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0572&lt;/td&gt;
&lt;td style="text-align: right;"&gt;120 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary_ToArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;214.85 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.389 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.084 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;214.84 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.93&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0572&lt;/td&gt;
&lt;td style="text-align: right;"&gt;120 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;FilterArray&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;.NET 5.0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;204.77 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;2.235 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;1.981 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;204.25 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.81&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.02&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.0496&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;104 B&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;335.74 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.112 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.911 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;335.81 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.34&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0725&lt;/td&gt;
&lt;td style="text-align: right;"&gt;152 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;416.95 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.229 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.085 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;415.96 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.66&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;160 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;437.06 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6.477 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6.058 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;438.21 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.74&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;160 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterArray_ToArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;338.20 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5.587 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.953 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;339.08 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.34&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.1988&lt;/td&gt;
&lt;td style="text-align: right;"&gt;416 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList_ToArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;358.90 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.098 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.422 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;358.15 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.42&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.2103&lt;/td&gt;
&lt;td style="text-align: right;"&gt;440 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet_ToArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;557.83 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.063 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.800 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;558.38 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.22&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.06&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.2213&lt;/td&gt;
&lt;td style="text-align: right;"&gt;464 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary_ToArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;574.07 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;7.046 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6.246 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;574.29 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.28&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.05&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.2213&lt;/td&gt;
&lt;td style="text-align: right;"&gt;464 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;215.45 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.700 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.419 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;215.22 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.86&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0496&lt;/td&gt;
&lt;td style="text-align: right;"&gt;104 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;320.02 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.990 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.651 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;319.63 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.27&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0725&lt;/td&gt;
&lt;td style="text-align: right;"&gt;152 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;437.02 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.356 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.861 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;436.05 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.74&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.05&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;160 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;452.16 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;8.495 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;7.947 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;453.44 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.80&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.06&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;160 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterArray_ToArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;340.41 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.727 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.190 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;339.43 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.35&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.1988&lt;/td&gt;
&lt;td style="text-align: right;"&gt;416 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList_ToArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;353.83 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6.133 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;7.532 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;354.83 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.42&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.2103&lt;/td&gt;
&lt;td style="text-align: right;"&gt;440 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet_ToArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;576.33 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;8.148 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;7.223 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;575.38 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.29&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.06&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.2213&lt;/td&gt;
&lt;td style="text-align: right;"&gt;464 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary_ToArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;584.74 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;11.442 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;10.143 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;580.87 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.32&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.08&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.2213&lt;/td&gt;
&lt;td style="text-align: right;"&gt;464 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;248.80 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.782 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6.546 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;247.30 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0572&lt;/td&gt;
&lt;td style="text-align: right;"&gt;120 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;329.58 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.299 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.925 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;328.61 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.31&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0725&lt;/td&gt;
&lt;td style="text-align: right;"&gt;152 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;443.56 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;8.577 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;9.177 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;441.12 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.77&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.07&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;160 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;461.62 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;7.660 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6.791 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;459.31 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.83&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.06&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;160 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterArray_ToArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;469.41 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;7.360 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6.525 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;470.37 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.86&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.2332&lt;/td&gt;
&lt;td style="text-align: right;"&gt;489 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList_ToArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;575.07 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6.210 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5.809 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;575.53 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.29&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.06&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.2403&lt;/td&gt;
&lt;td style="text-align: right;"&gt;505 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet_ToArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;686.90 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;13.321 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;14.253 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;687.12 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.75&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.2518&lt;/td&gt;
&lt;td style="text-align: right;"&gt;530 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary_ToArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;677.82 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;10.143 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;9.488 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;675.06 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.70&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.08&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.2518&lt;/td&gt;
&lt;td style="text-align: right;"&gt;530 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;FilterArray&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;.NET 5.0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;50&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;519.68 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;8.640 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;7.215 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;517.65 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.89&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.01&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.0496&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;104 B&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;819.43 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;11.886 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;11.118 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;814.16 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.41&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0725&lt;/td&gt;
&lt;td style="text-align: right;"&gt;152 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;972.46 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5.558 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5.199 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;973.23 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.67&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;160 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;972.48 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.824 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.359 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;972.40 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.67&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;160 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterArray_ToArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;810.60 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;12.451 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;11.647 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;808.18 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.39&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.4930&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,032 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList_ToArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;851.79 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;11.612 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;10.294 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;848.90 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.46&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.5045&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,056 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet_ToArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,479.79 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;28.211 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;26.389 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,471.44 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.54&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.06&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.5684&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,192 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary_ToArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,401.39 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;26.216 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;24.522 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,396.34 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.41&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.05&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.5684&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,192 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;534.26 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5.393 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.781 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;533.28 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.92&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0496&lt;/td&gt;
&lt;td style="text-align: right;"&gt;104 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;775.65 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6.106 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5.413 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;775.77 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.33&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0725&lt;/td&gt;
&lt;td style="text-align: right;"&gt;152 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,016.81 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;15.748 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;14.731 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,020.79 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.74&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;160 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,056.04 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;20.453 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;26.595 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,056.13 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.83&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.05&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;160 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterArray_ToArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;940.75 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;14.501 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;13.564 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;942.47 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.61&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.4921&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,032 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList_ToArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;977.71 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;19.087 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;34.417 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;983.53 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.67&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.08&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.5035&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,056 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet_ToArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,453.36 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;27.352 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;25.585 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,443.28 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.05&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.5684&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,192 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary_ToArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,368.01 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;11.451 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;10.151 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,365.62 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.35&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.5684&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,192 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;582.74 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.555 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.969 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;582.90 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0572&lt;/td&gt;
&lt;td style="text-align: right;"&gt;120 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;783.91 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.892 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.580 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;784.02 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.35&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0725&lt;/td&gt;
&lt;td style="text-align: right;"&gt;152 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,071.60 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;21.374 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;28.533 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,072.29 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.83&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.05&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;160 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,139.82 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;21.384 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;20.003 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,141.93 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.96&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;160 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterArray_ToArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,367.50 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;27.057 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;65.861 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,331.99 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.52&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.08&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.7381&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,549 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList_ToArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,597.52 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;23.829 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;19.899 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,603.19 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.74&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.7458&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,565 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet_ToArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,808.94 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;23.400 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;21.888 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,811.74 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.11&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.7572&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,589 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary_ToArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,832.78 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;34.906 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;34.282 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,840.36 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.14&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.07&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.7572&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,589 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;FilterArray&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;.NET 5.0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;1,049.10 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;10.403 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;8.687 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;1,047.02 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.87&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.02&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.0496&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;104 B&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,516.00 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;29.028 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;27.153 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,510.02 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.26&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0725&lt;/td&gt;
&lt;td style="text-align: right;"&gt;152 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,938.09 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;29.526 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;27.619 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,924.95 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.61&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;160 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,045.10 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;15.618 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;14.609 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,035.70 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.70&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;160 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterArray_ToArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,499.65 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;21.245 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;19.872 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,498.60 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.25&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.8869&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,856 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList_ToArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,555.10 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;31.036 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;31.871 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,560.42 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.30&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.8984&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,880 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet_ToArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,457.19 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;34.939 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;30.973 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,451.15 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.05&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.0147&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,128 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary_ToArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,599.85 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;39.720 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;37.155 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,585.06 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.17&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.0147&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,128 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,102.10 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.401 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.117 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,101.34 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.92&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0496&lt;/td&gt;
&lt;td style="text-align: right;"&gt;104 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,423.55 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.328 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.599 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,423.44 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.19&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0725&lt;/td&gt;
&lt;td style="text-align: right;"&gt;152 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,160.32 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;42.036 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;51.624 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,159.38 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.81&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.06&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;160 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,190.92 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;42.564 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;53.830 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,194.61 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.81&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.05&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;160 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterArray_ToArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,530.44 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;20.335 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;19.021 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,526.51 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.28&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.8869&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,856 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList_ToArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,583.65 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;31.314 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;36.061 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,568.48 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.32&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.05&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.8984&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,880 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet_ToArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,711.94 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;54.067 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;127.443 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,673.45 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.33&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.16&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.0147&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,128 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary_ToArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,509.05 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;43.450 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;40.643 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,508.83 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.09&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.0147&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,128 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,199.84 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;21.589 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;19.139 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,195.87 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0572&lt;/td&gt;
&lt;td style="text-align: right;"&gt;120 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,614.03 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;31.386 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;27.823 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,602.08 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.35&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0725&lt;/td&gt;
&lt;td style="text-align: right;"&gt;152 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,035.95 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;39.087 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;32.640 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,023.10 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.70&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;160 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,295.34 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;9.565 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;8.947 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,296.43 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.91&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;160 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterArray_ToArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,767.37 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;17.560 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;16.426 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,768.58 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.31&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.4305&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3,001 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList_ToArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3,164.04 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;11.241 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;9.386 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3,164.16 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.64&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.4381&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3,017 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet_ToArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3,660.94 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;70.542 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;65.985 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3,654.40 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.06&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.08&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.4458&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3,041 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary_ToArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3,699.41 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;21.608 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;18.044 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3,693.30 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.08&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.4458&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3,041 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;FilterArray&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;.NET 5.0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;500&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;5,357.99 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;103.933 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;149.057 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;5,286.35 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.93&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.03&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.0458&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;104 B&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td style="text-align: right;"&gt;7,633.34 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;81.790 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;72.504 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;7,610.14 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.33&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0610&lt;/td&gt;
&lt;td style="text-align: right;"&gt;152 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td style="text-align: right;"&gt;9,670.59 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;67.964 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;53.062 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;9,665.57 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.68&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;160 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td style="text-align: right;"&gt;9,571.07 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;112.495 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;93.938 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;9,531.54 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.67&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;160 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterArray_ToArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6,904.08 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;66.606 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;62.303 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6,874.80 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.0054&lt;/td&gt;
&lt;td style="text-align: right;"&gt;8,392 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList_ToArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6,968.44 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;83.854 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;70.022 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6,975.29 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.21&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.0131&lt;/td&gt;
&lt;td style="text-align: right;"&gt;8,416 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet_ToArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td style="text-align: right;"&gt;10,971.07 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;109.378 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;96.961 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;10,951.74 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.91&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.0741&lt;/td&gt;
&lt;td style="text-align: right;"&gt;8,536 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary_ToArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td style="text-align: right;"&gt;11,998.38 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;211.175 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;197.533 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;11,998.62 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.09&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.0741&lt;/td&gt;
&lt;td style="text-align: right;"&gt;8,536 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5,347.93 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;33.686 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;28.129 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5,338.97 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.93&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0458&lt;/td&gt;
&lt;td style="text-align: right;"&gt;104 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td style="text-align: right;"&gt;7,483.89 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;45.716 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;35.692 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;7,498.14 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.30&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0610&lt;/td&gt;
&lt;td style="text-align: right;"&gt;152 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td style="text-align: right;"&gt;9,377.21 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;116.563 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;97.335 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;9,337.82 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.63&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;160 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td style="text-align: right;"&gt;9,933.11 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;61.978 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;51.755 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;9,907.55 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.73&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;160 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterArray_ToArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td style="text-align: right;"&gt;7,688.61 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;149.282 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;223.439 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;7,655.68 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.35&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.0054&lt;/td&gt;
&lt;td style="text-align: right;"&gt;8,392 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList_ToArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td style="text-align: right;"&gt;8,520.42 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;170.171 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;315.423 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;8,541.70 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.48&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.05&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.0131&lt;/td&gt;
&lt;td style="text-align: right;"&gt;8,416 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet_ToArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td style="text-align: right;"&gt;11,357.74 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;167.598 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;139.952 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;11,288.67 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.98&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.0741&lt;/td&gt;
&lt;td style="text-align: right;"&gt;8,536 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary_ToArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td style="text-align: right;"&gt;11,174.15 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;183.748 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;180.465 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;11,096.25 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.95&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.0741&lt;/td&gt;
&lt;td style="text-align: right;"&gt;8,536 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5,735.48 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;35.151 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;39.070 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5,726.53 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0534&lt;/td&gt;
&lt;td style="text-align: right;"&gt;120 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td style="text-align: right;"&gt;8,031.11 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;136.072 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;186.257 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;7,934.82 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.40&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0610&lt;/td&gt;
&lt;td style="text-align: right;"&gt;153 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td style="text-align: right;"&gt;10,124.26 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;199.856 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;266.803 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;10,041.80 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.78&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;161 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td style="text-align: right;"&gt;11,615.37 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;147.565 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;123.223 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;11,591.23 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0763&lt;/td&gt;
&lt;td style="text-align: right;"&gt;161 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterArray_ToArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td style="text-align: right;"&gt;13,573.07 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;128.080 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;113.540 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;13,595.41 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.36&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5.9052&lt;/td&gt;
&lt;td style="text-align: right;"&gt;12,434 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterList_ToArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td style="text-align: right;"&gt;17,131.55 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;340.744 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;510.009 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;17,036.32 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.98&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.09&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5.9204&lt;/td&gt;
&lt;td style="text-align: right;"&gt;12,445 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterHashSet_ToArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td style="text-align: right;"&gt;18,801.99 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;353.812 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;295.449 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;18,881.88 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.28&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.06&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5.9204&lt;/td&gt;
&lt;td style="text-align: right;"&gt;12,470 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FilterDictionary_ToArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td style="text-align: right;"&gt;18,045.81 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;133.857 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;118.661 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;18,032.49 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.14&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5.9204&lt;/td&gt;
&lt;td style="text-align: right;"&gt;12,470 B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre&gt;&lt;code&gt;// * Legends *
  N         : Value of the 'N' parameter
  Mean      : Arithmetic mean of all measurements
  Error     : Half of 99.9% confidence interval
  StdDev    : Standard deviation of all measurements
  Ratio     : Mean of the ratio distribution ([Current]/[Baseline])
  RatioSD   : Standard deviation of the ratio distribution ([Current]/[Baseline])
  Gen 0     : GC Generation 0 collects per 1000 operations
  Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)
  1 us      : 1 Microsecond (0.000001 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;p&gt;The following diagram shows execution time of both methods (in microseconds) for .NET 5 platform:
&lt;img src="https://blog.rogatnev.net/img/collections/array-to-array-net5-diagram-time.png" alt="Time diagram"&gt;&lt;/p&gt;
&lt;p&gt;The results are quite comparable, but .NET Framework is not so good:
&lt;img src="https://blog.rogatnev.net/img/collections/array-to-array-net48-diagram-time.png" alt="Time diagram"&gt;&lt;/p&gt;
&lt;p&gt;Additional &lt;code&gt;ToArray()&lt;/code&gt; call is about 2.5 times slower.&lt;/p&gt;
&lt;p&gt;.NET made huge improvements in Core 3.1 and 5 versions. The following diagram shows execution time (in microseconds) of different .NET versions depending on the number of elements:
&lt;img src="https://blog.rogatnev.net/img/collections/array-to-array-difference-diagram-time.png" alt="Time diagram"&gt;&lt;/p&gt;
&lt;h2 id="array-arguments"&gt;Array arguments&lt;/h2&gt;
&lt;p&gt;We used to use arrays as arguments in methods, but this can be a place where we can make another unnecessary transformation. Let's look at the example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;private async Task&amp;lt;OrderPackage[]&amp;gt; ReadRootPackagesAsync(params Guid[] packageIds)
{
	var queryPackageIds = new HashSet&amp;lt;Guid&amp;gt;(packageIds);
	...
	var packageIds = queryPackageIds.ToArray();
	var orderPackages = await orderPackageService.SelectByIdsAsync(packageIds).ConfigureAwait(false);
	...
}

public Task&amp;lt;OrderPackage[]&amp;gt; SelectByIdsAsync(params Guid[] packageIds)
{
	...
	return GetTable().Where(o =&amp;gt; packageIds.Contains(o.Id));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;GetTable()&lt;/code&gt; is a method that returns &lt;code&gt;IQueryable&amp;lt;OrderPackage&amp;gt;&lt;/code&gt;, so the &lt;code&gt;Where&lt;/code&gt; expression will be converted to a SQL code, and it's not necessary to use any specific collection. Here we can use &lt;code&gt;IEnumerable&amp;lt;Guid&amp;gt;&lt;/code&gt; again, but I prefer an interface &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.ireadonlycollection-1?view=net-6.0"&gt;&lt;code&gt;IReadOnlyCollection&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/a&gt; that was introduced in .NET 4.5 Framework. The &lt;code&gt;IReadOnlyCollection&amp;lt;T&amp;gt;&lt;/code&gt; interface indicates that a collection is materialized unlike &lt;code&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt; but we are not interested in a collection's structure (HashSet, List, Array, ... - does not matter). And on the other hand, using the &lt;code&gt;IReadOnlyCollection&amp;lt;T&amp;gt;&lt;/code&gt; prevents the &lt;a href="https://www.jetbrains.com/help/resharper/PossibleMultipleEnumeration.html"&gt;possibility of multiple enumeration of &lt;code&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/a&gt;.
So, our modified example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;private async Task&amp;lt;OrderPackage[]&amp;gt; ReadRootPackagesAsync(params Guid[] packageIds)
{
	var queryPackageIds = new HashSet&amp;lt;Guid&amp;gt;(packageIds);
	...
	var orderPackages = await orderPackageService.SelectByIdsAsync(queryPackageIds).ConfigureAwait(false);
	...
}

public Task&amp;lt;OrderPackage[]&amp;gt; SelectByIdsAsync(IReadOnlyCollection&amp;lt;Guid&amp;gt; packageIds)
{
	...
	return GetTable().Where(o =&amp;gt; packageIds.Contains(o.Id));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We pass the &lt;code&gt;HashSet&amp;lt;Guid&amp;gt;&lt;/code&gt; directly to the &lt;code&gt;SelectByIdsAsync&lt;/code&gt; method without using intermediate variables.&lt;/p&gt;
&lt;p&gt;But we should be careful using common interfaces like &lt;code&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt; or &lt;code&gt;IReadOnlyCollection&amp;lt;T&amp;gt;&lt;/code&gt; to avoid antipatterns like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public UserSearchModel BuildUserSearchModel(IEnumerable&amp;lt;IUserProps&amp;gt; userPropses)
{
	var users = new List&amp;lt;UserModel&amp;gt;();
	var allUserPropses = userPropses.ToArray();
	foreach (var userProps in allUserPropses)
	{
		...
	}
	
	return new UserSearchModel
	{
		Users = users.ToArray(),
		QueryModel = queryModel,
		TotalCount = total,
		AvailableCount = users.Count,
		HasMore = allUserPropses.Length &amp;gt;= queryModel.PageSize,
		QueryForm = queryFormBuilder.Build(queryModel)
	};
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The argument &lt;code&gt;userPropses&lt;/code&gt; converted to a local array &lt;code&gt;allUserPropses&lt;/code&gt; to avoid multiple enumeration as we learned before. &lt;code&gt;userPropses&lt;/code&gt; has type &lt;code&gt;IEnumerable&amp;lt;IUserProps&amp;gt;&lt;/code&gt;, so it deserves our attention. But what we will see, if we look at the &lt;code&gt;BuildUserSearchModel&lt;/code&gt; call?&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public async Task&amp;lt;SearchModel&amp;gt; SearchAsync(SearchQuery searchQuery)
{
	var userPropses = new List&amp;lt;IUserProps&amp;gt;();
	...
	return searchModelBuilder.BuildUserSearchModel(userPropses);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It becomes obvious that &lt;code&gt;IEnumerable&amp;lt;IUserProps&amp;gt;&lt;/code&gt; is not the best choice. It confuses developers because it says that &lt;code&gt;userPropses&lt;/code&gt; is not a collection, but it is in fact. The better variant - replace it with &lt;code&gt;IReadOnlyCollection&amp;lt;IUserProps&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public UserSearchModel BuildUserSearchModel(IReadOnlyCollection&amp;lt;IUserProps&amp;gt; userPropses)
{
	var users = new List&amp;lt;UserModel&amp;gt;();
	foreach (var userProps in userPropses)
	{
		...
	}
	
	return new UserSearchModel
	{
		Users = users.ToArray(),
		QueryModel = queryModel,
		TotalCount = total,
		AvailableCount = users.Count,
		HasMore = userPropses.Count &amp;gt;= queryModel.PageSize,
		QueryForm = queryFormBuilder.Build(queryModel)
	};
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Code became cleaner, no conversions, no overhead.&lt;/p&gt;
&lt;h2 id="fluent-assertions-in-unit-tests"&gt;Fluent assertions in unit tests&lt;/h2&gt;
&lt;p&gt;I would like to make a small note about the &lt;a href="https://fluentassertions.com/"&gt;Fluent Assertions&lt;/a&gt; library.
We write many tests where we need to compare two collections:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;[Test]
public void TestSingleWriteInSameTimeline()
{
	var testData = new List&amp;lt;TestTimelineData&amp;gt;();
	...
	
	var actual = timeline.Select&amp;lt;TestTimelineData&amp;gt;(timelineId, ticks, TestWriteCount);

	actual.Select(x =&amp;gt; x.Data).ToArray().Should().BeEquivalentTo(testData.ToArray());
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But the method &lt;code&gt;BeEquivalentTo&lt;/code&gt; works fine with enumerations. We can verify this by looking at its definition:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public AndConstraint&amp;lt;TAssertions&amp;gt; BeEquivalentTo&amp;lt;TExpectation&amp;gt;(IEnumerable&amp;lt;TExpectation&amp;gt; expectation,
	string because = "", params object[] becauseArgs)
{
	return BeEquivalentTo(expectation, config =&amp;gt; config, because, becauseArgs);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Fluent Assertions will create arrays anyway, so you will get few performance benefits but still can keep your code cleaner:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;[Test]
public void TestSingleWriteInSameTimeline()
{
	var testData = new List&amp;lt;TestTimelineData&amp;gt;();
	...
	
	var actual = timeline.Select&amp;lt;TestTimelineData&amp;gt;(timelineId, ticks, TestWriteCount);

	actual.Select(x =&amp;gt; x.Data).Should().BeEquivalentTo(testData));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id="analyzer"&gt;Analyzer&lt;/h1&gt;
&lt;p&gt;If you are worried about performance and memory management or just purity of code, you should be very attentive with collection manipulations. Sometimes, it's challenging to notice a problem, especially after refactorings: you change a method signature from &lt;code&gt;int[]&lt;/code&gt; to &lt;code&gt;IEnumerable&amp;lt;int&amp;gt;&lt;/code&gt; but don't check all callings - it can lead to some issues like the ones we reviewed earlier.&lt;/p&gt;
&lt;p&gt;Unfortunately, there is not a built-in tool to detect problems like this because many of them need complex analysis. I started &lt;a href="https://github.com/Backs/Collections.Analyzer"&gt;Collections.Analyzer&lt;/a&gt; project that can warn about potential problems:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/collections/collections-linq-example-1.gif" alt="LINQ example"&gt;&lt;/p&gt;
&lt;p&gt;And more obvious cases:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/collections/collections-linq-example-2.gif" alt="LINQ example"&gt;&lt;/p&gt;
&lt;h1 id="links"&gt;Links&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.ireadonlycollection-1?view=net-6.0"&gt;&lt;code&gt;IReadOnlyCollection&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.jetbrains.com/help/resharper/PossibleMultipleEnumeration.html"&gt;Code Inspection: Possible multiple enumeration of &lt;code&gt;IEnumerable&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fluentassertions.com/"&gt;Fluent Assertions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Backs/Collections.Analyzer"&gt;Collections.Analyzer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>Harmful collection transformations. Part 2: automatic diagnostics</title>
			<link>https://blog.rogatnev.net/posts/en/2021/10/Harmful-collection-transformations-part-2-diagnostics.html</link>
			<description>Discover how Roslyn analyzers can automatically detect inefficient collection transformations in C# code, such as redundant ToArray and ToCharArray calls, with real-world examples and IDE integration for quick fixes.</description>
			<author>Sergey Rogatnev</author>
			<guid>https://blog.rogatnev.net/posts/en/2021/10/Harmful-collection-transformations-part-2-diagnostics.html</guid>
			<pubDate>Wed, 13 Oct 2021 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;The &lt;a href="xref:2021-09-23-Harmful-collection-transformations-part-1-strings"&gt;previous post&lt;/a&gt; contains code examples that can be simplified by removing &lt;code&gt;ToArray()&lt;/code&gt; or &lt;code&gt;ToCharArray()&lt;/code&gt; calls. And an interesting example with &lt;code&gt;List&amp;lt;char&amp;gt;&lt;/code&gt; constructor. But we need to be very attentive to find such places in our code. It would be much safer to do it automatically. This post shows us about automatic compiler diagnostics that can detect issues.&lt;/p&gt;
&lt;!--more--&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="xref:2021-09-23-Harmful-collection-transformations-part-1-strings"&gt;Part 1: string to array of chars&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 2: automatic diagnostics&lt;/li&gt;
&lt;li&gt;&lt;a href="xref:2022-01-08-Harmful-collection-transformations-part-3-collections"&gt;Part 3: collections&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="analyzers"&gt;Analyzers&lt;/h1&gt;
&lt;p&gt;Since the release of the new .NET compiler platform, Roslyn developers have got powerful tools for code analysis. Roslyn compilers offer APIs for C# and Visual Basic that we can use for code-related applications. One of the possible applications is a code analyzer that produces warnings and can offer an automatic code fix in IDE.&lt;/p&gt;
&lt;p&gt;One of the most popular analyzers built on Roslyn is &lt;a href="https://www.nuget.org/packages/ConfigureAwaitChecker.Analyzer/"&gt;ConfigureAwaitChecker.Analyzer&lt;/a&gt;. The analyzer helps to find awaited methods without &lt;code&gt;ConfigureAwait(false)&lt;/code&gt; call and offers automatic code fix.&lt;/p&gt;
&lt;h1 id="collections.analyzer"&gt;Collections.Analyzer&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://www.nuget.org/packages/Collections.Analyzer/"&gt;Collections.Analyzer&lt;/a&gt; is a set of roslyn-based diagnostics for C#-projects that detect potential issues with different collections and produce warnings.&lt;/p&gt;
&lt;h2 id="installation"&gt;Installation&lt;/h2&gt;
&lt;p&gt;Every analyzer can be installed as a usual nuget-package. Add a package reference to a project:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;PackageReference Include="Collections.Analyzer" Version="0.1.9" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The analyzer will work only in the project it was added to. If you want to analyze all projects in your solution, you can add file &lt;code&gt;Directory.build.props&lt;/code&gt; to the solution directory with content:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Project&amp;gt;
  &amp;lt;ItemGroup&amp;gt;
    &amp;lt;PackageReference Include="Collections.Analyzer" Version="0.1.9" /&amp;gt;
  &amp;lt;/ItemGroup&amp;gt;
&amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;MSBuild will read these properties.&lt;/p&gt;
&lt;h2 id="build"&gt;Build&lt;/h2&gt;
&lt;p&gt;If your code has problems, you'll see warnings as usual.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;PS C:\example&amp;gt; dotnet build
C:\example\App\Program.cs(12,31): warning CI0001: Redundant string conversion [C:\example\App\App.csproj]
C:\example\App\Program.cs(17,21): warning CI0001: Redundant string conversion [C:\example\App\App.csproj]
  App -&amp;gt; C:\example\App\bin\Debug\net472\App.exe

    Warnings: 2
    Errors: 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;CI0001: Redundant string conversion&lt;/code&gt; is a new diagnostic described in the Collections.Analyzer package.&lt;/p&gt;
&lt;h2 id="ide"&gt;IDE&lt;/h2&gt;
&lt;p&gt;Besides compiler warnings, all diagnostics are integrated in IDE (Visual Studio or Rider) with automatic code fixes. So, we can correct our code on the fly.
Let's see how it works for strings.&lt;/p&gt;
&lt;h3 id="linq"&gt;LINQ&lt;/h3&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/collections/string-example-1.gif" alt="String ToCharArray"&gt;&lt;/p&gt;
&lt;h3 id="foreach-statement"&gt;foreach statement&lt;/h3&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/collections/string-example-2.gif" alt="String ToCharArray"&gt;&lt;/p&gt;
&lt;h3 id="listchar-constructor"&gt;&lt;code&gt;List&amp;lt;char&amp;gt;&lt;/code&gt; constructor&lt;/h3&gt;
&lt;p&gt;The case with &lt;code&gt;List&amp;lt;char&amp;gt;&lt;/code&gt; constructor is very seldom and specific, but analyzer always helps us:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/collections/string-example-3.gif" alt="String ToCharArray"&gt;&lt;/p&gt;
&lt;h3 id="linq-on-method"&gt;LINQ on method&lt;/h3&gt;
&lt;p&gt;Analyzer works on methods that return strings:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/collections/string-example-4.gif" alt="String ToCharArray"&gt;&lt;/p&gt;
&lt;p&gt;These are the most popular string-to-array issues that I have encountered. But if you have any ideas, feel free to write to me or add them to &lt;a href="https://github.com/Backs/Collections.Analyzer/issues"&gt;GitHub issues&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the next article we will continue to look at other collections, possible issues and add automatic diagnostics if it's possible.&lt;/p&gt;
&lt;h1 id="links"&gt;Links&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Backs/Collections.Analyzer"&gt;Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nuget.org/packages/Collections.Analyzer/"&gt;Nuget&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/visualstudio/msbuild/customize-your-build"&gt;Customize your build&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/visualstudio/extensibility/getting-started-with-roslyn-analyzers"&gt;Get started with Roslyn analyzers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
		<item>
			<title>Harmful collection transformations. Part 1: string to array of chars</title>
			<link>https://blog.rogatnev.net/posts/en/2021/09/Harmful-collection-transformations-part-1-strings.html</link>
			<description>Learn why converting strings to arrays of chars in C# is usually unnecessary and can hurt performance. See benchmark results and get recommendations for more efficient string and collection handling.</description>
			<author>Sergey Rogatnev</author>
			<guid>https://blog.rogatnev.net/posts/en/2021/09/Harmful-collection-transformations-part-1-strings.html</guid>
			<pubDate>Thu, 23 Sep 2021 00:00:00 GMT</pubDate>
			<content:encoded>&lt;p&gt;An array — the simplest collection. It's just a memory block with elements of one type. Arrays are often used when we need to iterate a collection, to search for elements or transform them. C# and .net evolved, many new collections were introduced, interfaces allowed to create generalized algorithms, LINQ simplified the work of programmers, but we continue to use arrays in inappropriate places and perform excess and sometimes harmful transformations.
This post begins a series about collections, their features, performance and design. All posts will contain benchmarks, code examples, my opinion and recommendations.&lt;/p&gt;
&lt;p&gt;I want to start with a &lt;code&gt;string&lt;/code&gt;, some specific collection of .net.&lt;/p&gt;
&lt;!--more--&gt;
&lt;ul&gt;
&lt;li&gt;Part 1: string to an array of chars&lt;/li&gt;
&lt;li&gt;&lt;a href="xref:2021-10-13-Harmful-collection-transformations-part-2-diagnostics"&gt;Part 2: automatic diagnostics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="xref:2022-01-08-Harmful-collection-transformations-part-3-collections"&gt;Part 3: collections&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="string"&gt;String&lt;/h1&gt;
&lt;p&gt;Any large enough project operates with strings. Usually strings store a user input and are used as data for other types. For example, we can read text input and convert it to a number type:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;static void Main()
{
    var input = Console.ReadLine();
    var value = int.Parse(input);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All we know, user input must be validated. Here we need to check that all chars are digits. It's a widespread check. And often the following code is used:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;private bool IsDigitString(string str)
{
   return str.ToCharArray().All(char.IsDigit);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;string bik = ...;
if (bik.ToArray().Any(x =&amp;gt; !char.IsDigit(x)))
{
   message = "BIK must contain only digits";
   return false;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Looks simple: get a collection of chars and call LINQ-method to check every digit. The only unnecessary and harmful thing is transformation of string to an array of chars with methods &lt;code&gt;ToCharArray()&lt;/code&gt; or &lt;code&gt;ToArray()&lt;/code&gt;. We used to use LINQ-methods with collections, but forget that the string is an immutable collection that implements &lt;code&gt;IEnumerable&amp;lt;char&amp;gt;&lt;/code&gt; interface:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;[Serializable]
public sealed partial class String : IComparable, IEnumerable, IConvertible, IEnumerable&amp;lt;char&amp;gt;, IComparable&amp;lt;string&amp;gt;, IEquatable&amp;lt;string&amp;gt;, ICloneable
{
...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, we can modify our examples and remove excessive calls:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;private bool IsDigitString(string str)
{
   return str.All(char.IsDigit);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;string bik = ...;
if (bik.Any(x =&amp;gt; !char.IsDigit(x)))
{
   message = "BIK must contain only digits";
   return false;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ok, our code became simpler and more readable. But what do we get besides code quality benefits? Benchmarks can help us. Let's compare three methods: with &lt;code&gt;ToArray()&lt;/code&gt;, with &lt;code&gt;ToCharArray()&lt;/code&gt; and without additional calls for different string lengths and frameworks:&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Benchmark code. Click to expand.&lt;/summary&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;namespace ToCharArrayBenchmark
{
    using System.Linq;
    using BenchmarkDotNet.Attributes;
    using BenchmarkDotNet.Jobs;

    [SimpleJob(RuntimeMoniker.Net48, baseline: true)]
    [SimpleJob(RuntimeMoniker.NetCoreApp31)]
    [SimpleJob(RuntimeMoniker.Net50)]
    [DisassemblyDiagnoser]
    [MemoryDiagnoser]
    public class IsDigitBenchmark
    {
        [Params(10, 100, 1000)]
        public int N { get; set; }

        private string value;

        [GlobalSetup]
        public void SetUp()
        {
            var chars = new char[N];
            for (int i = 0; i &amp;lt; N; i++)
            {
                chars[i] = (char)(i % 10 + '0');
            }

            value = new string(chars);
        }

        [Benchmark]
        public bool IsDigitString_ToCharArray()
        {
            return value.ToCharArray().All(char.IsDigit);
        }

        [Benchmark]
        public bool IsDigitString_ToArray()
        {
            return value.ToArray().All(char.IsDigit);
        }

        [Benchmark(Baseline = true)]
        public bool IsDigitString()
        {
            return value.All(char.IsDigit);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;Full results in table format. Click to expand.&lt;/summary&gt;
&lt;pre&gt;&lt;code class="language-ini"&gt;BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19043.1237 (21H1/May2021Update)
AMD Ryzen 7 4700U with Radeon Graphics, 1 CPU, 8 logical and 8 physical cores
  [Host]             : .NET Framework 4.8 (4.8.4400.0), X64 RyuJIT
  .NET 5.0           : .NET 5.0.7 (5.0.721.25508), X64 RyuJIT
  .NET Core 3.1      : .NET Core 3.1.11 (CoreCLR 4.700.20.56602, CoreFX 4.700.20.56604), X64 RyuJIT
  .NET Framework 4.8 : .NET Framework 4.8 (4.8.4400.0), X64 RyuJIT
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Runtime&lt;/th&gt;
&lt;th&gt;N&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Mean&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Error&lt;/th&gt;
&lt;th style="text-align: right;"&gt;StdDev&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Ratio&lt;/th&gt;
&lt;th style="text-align: right;"&gt;RatioSD&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Gen 0&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Allocated&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IsDigitString&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;.NET 5.0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;94.54 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;1.924 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;3.267 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;1.01&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.04&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.0459&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;96 B&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsDigitString_ToCharArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;97.01 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.602 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.503 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0688&lt;/td&gt;
&lt;td style="text-align: right;"&gt;144 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsDigitString_ToArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;250.71 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.346 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.794 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.64&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.1376&lt;/td&gt;
&lt;td style="text-align: right;"&gt;288 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsDigitString&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;98.74 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.595 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.492 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0459&lt;/td&gt;
&lt;td style="text-align: right;"&gt;96 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsDigitString_ToCharArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;105.02 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.217 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.079 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0688&lt;/td&gt;
&lt;td style="text-align: right;"&gt;144 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsDigitString_ToArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;294.50 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.838 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.700 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.11&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.05&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.1373&lt;/td&gt;
&lt;td style="text-align: right;"&gt;288 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsDigitString&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;94.96 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.863 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.913 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0459&lt;/td&gt;
&lt;td style="text-align: right;"&gt;96 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsDigitString_ToCharArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;90.48 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.345 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.258 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.95&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0688&lt;/td&gt;
&lt;td style="text-align: right;"&gt;144 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsDigitString_ToArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;249.17 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.617 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.482 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.63&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.1452&lt;/td&gt;
&lt;td style="text-align: right;"&gt;305 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IsDigitString&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;.NET 5.0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;617.01 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;2.901 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;2.714 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.91&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.02&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.0458&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;96 B&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsDigitString_ToCharArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;730.71 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6.392 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5.666 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.08&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.1526&lt;/td&gt;
&lt;td style="text-align: right;"&gt;320 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsDigitString_ToArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,471.00 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;25.149 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;62.630 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.21&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.16&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.3891&lt;/td&gt;
&lt;td style="text-align: right;"&gt;816 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsDigitString&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;695.67 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;13.740 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;22.576 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0458&lt;/td&gt;
&lt;td style="text-align: right;"&gt;96 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsDigitString_ToCharArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;707.81 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;14.127 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;40.760 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.12&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.05&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.1526&lt;/td&gt;
&lt;td style="text-align: right;"&gt;320 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsDigitString_ToArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,638.57 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;18.990 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;16.834 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.42&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.05&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.3891&lt;/td&gt;
&lt;td style="text-align: right;"&gt;816 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsDigitString&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;677.60 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;13.072 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;12.839 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0458&lt;/td&gt;
&lt;td style="text-align: right;"&gt;96 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsDigitString_ToCharArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;692.45 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;10.962 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;9.717 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.1526&lt;/td&gt;
&lt;td style="text-align: right;"&gt;321 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsDigitString_ToArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,696.70 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;26.955 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;22.509 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.51&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.05&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.4768&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,003 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IsDigitString&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;.NET 5.0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1000&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;8,340.66 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;159.513 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;141.404 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;1.26&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.02&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.0458&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;96 B&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsDigitString_ToCharArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;7,902.88 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;153.280 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;209.812 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.20&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.0071&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,120 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsDigitString_ToArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;12,526.66 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;215.331 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;201.421 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.90&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.1820&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4,568 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsDigitString&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6,771.54 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;42.026 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;35.093 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0458&lt;/td&gt;
&lt;td style="text-align: right;"&gt;96 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsDigitString_ToCharArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6,820.17 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;77.269 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;60.326 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.0071&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,120 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsDigitString_ToArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;14,993.39 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;249.113 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;255.821 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.27&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.1820&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4,568 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsDigitString&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6,609.99 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;51.380 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;48.061 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0458&lt;/td&gt;
&lt;td style="text-align: right;"&gt;96 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsDigitString_ToCharArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6,922.85 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;102.382 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;90.759 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.05&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.0071&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,127 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IsDigitString_ToArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;15,401.52 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;117.030 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;103.744 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.33&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.0823&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6,475 B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre&gt;&lt;code&gt;// * Legends *
  N         : Value of the 'N' parameter
  Mean      : Arithmetic mean of all measurements
  Error     : Half of 99.9% confidence interval
  StdDev    : Standard deviation of all measurements
  Ratio     : Mean of the ratio distribution ([Current]/[Baseline])
  RatioSD   : Standard deviation of the ratio distribution ([Current]/[Baseline])
  Gen 0     : GC Generation 0 collects per 1000 operations
  Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)
  1 us      : 1 Microsecond (0.000001 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;p&gt;Let's examine a diagram with execution times (in microseconds) for strings with 100 chars:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/collections/string-diagram-time.png" alt="Time diagram"&gt;&lt;/p&gt;
&lt;p&gt;As we see, method &lt;code&gt;IsDigitString&lt;/code&gt; is about 5% faster than calling &lt;code&gt;ToCharArray()&lt;/code&gt;. &lt;code&gt;ToArray()&lt;/code&gt; is up to 3 times slower.
Both &lt;code&gt;ToCharArray()&lt;/code&gt; and &lt;code&gt;ToArray()&lt;/code&gt; do the same, but &lt;code&gt;ToCharArray()&lt;/code&gt; is a &lt;code&gt;string&lt;/code&gt; class method and very optimized with unsafe code. &lt;code&gt;ToArray()&lt;/code&gt; is an extension method for &lt;code&gt;IEnumerable&amp;lt;T&amp;gt;&lt;/code&gt; and is not optimized so much.&lt;/p&gt;
&lt;p&gt;So, we know, there is no need to transform string to array to use LINQ-methods, this transformation slightly hurts performance and there is one more thing - GC. Every time when we call &lt;code&gt;ToArray()&lt;/code&gt; or &lt;code&gt;ToCharArray()&lt;/code&gt; we create at least one new object — a new array. We no longer need the source string, and GC will collect at some time.&lt;/p&gt;
&lt;p&gt;The next diagram shows us memory allocations (in bytes) for strings with 100 chars:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/collections/string-diagram-memory.png" alt="Allocated memory diagram"&gt;&lt;/p&gt;
&lt;h1 id="cons"&gt;Cons&lt;/h1&gt;
&lt;p&gt;Should we always remove &lt;code&gt;ToArray()&lt;/code&gt; and &lt;code&gt;ToCharArray()&lt;/code&gt; calls? It depends on context. &lt;code&gt;ToArray()&lt;/code&gt; generates a lot of overhead, so I can't find a situation where to use it. But as we saw before, &lt;code&gt;ToCharArray()&lt;/code&gt; is very fast, and almost does not affect performance. Can you imagine that calling &lt;code&gt;ToCharArray()&lt;/code&gt; will save time? But it does! Look at this example:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var convertParameterSymbols = attributes["ConvertParameterSymbols"];
if (convertParameterSymbols != null)
	ConvertParameterSymbols = new List&amp;lt;char&amp;gt;(convertParameterSymbols.ToCharArray());
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It looks like we need to remove &lt;code&gt;ToCharrArray()&lt;/code&gt;. But before doing this, we need to go back to the &lt;code&gt;string&lt;/code&gt; class definition. As we remember, it implements &lt;code&gt;IEnumerable&amp;lt;char&amp;gt;&lt;/code&gt;, but not &lt;code&gt;ICollection&amp;lt;char&amp;gt;&lt;/code&gt;.
&lt;code&gt;ICollection&amp;lt;T&amp;gt;&lt;/code&gt; interface describes the contract for mutable collections (&lt;code&gt;Add&lt;/code&gt;, &lt;code&gt;Clear&lt;/code&gt;, &lt;code&gt;Remove&lt;/code&gt;) that is not suitable for strings.
And look at &lt;code&gt;List&amp;lt;T&amp;gt;&lt;/code&gt; constructor:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public List(IEnumerable&amp;lt;T&amp;gt; collection)
{
  if (collection == null)
	ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
  if (collection is ICollection&amp;lt;T&amp;gt; objs)
  {
	int count = objs.Count;
	if (count == 0)
	{
	  this._items = List&amp;lt;T&amp;gt;._emptyArray;
	}
	else
	{
	  this._items = new T[count];
	  objs.CopyTo(this._items, 0);
	  this._size = count;
	}
  }
  else
  {
	this._size = 0;
	this._items = List&amp;lt;T&amp;gt;._emptyArray;
	foreach (T obj in collection)
	  this.Add(obj);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It's optimized for &lt;code&gt;ICollection&amp;lt;T&amp;gt;&lt;/code&gt; objects. Elements are just copied as a block with &lt;code&gt;CopyTo&lt;/code&gt; instead of adding one by one that causes resizes of the list. What types do implement &lt;code&gt;ICollection&amp;lt;T&amp;gt;&lt;/code&gt; interface? You will be surprised, but any array (&lt;code&gt;T[]&lt;/code&gt;) &lt;a href="https://referencesource.microsoft.com/#mscorlib/system/array.cs,2754"&gt;can be converted&lt;/a&gt; to &lt;code&gt;ICollection&amp;lt;T&amp;gt;&lt;/code&gt;. And using &lt;code&gt;new List&amp;lt;char&amp;gt;(convertParameterSymbols.ToCharArray())&lt;/code&gt; is faster than &lt;code&gt;new List&amp;lt;char&amp;gt;(convertParameterSymbols)&lt;/code&gt;. Benchmarks can prove this.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;Benchmark code. Click to expand.&lt;/summary&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;namespace ToCharArrayBenchmark
{
    using System.Collections.Generic;
    using System.Linq;
    using BenchmarkDotNet.Attributes;
    using BenchmarkDotNet.Jobs;

    [SimpleJob(RuntimeMoniker.Net48, baseline: true)]
    [SimpleJob(RuntimeMoniker.NetCoreApp31)]
    [SimpleJob(RuntimeMoniker.Net50)]
    [DisassemblyDiagnoser]
    [MemoryDiagnoser]
    public class ConstructorBenchmark
    {
        [Params(10, 100, 1000)]
        public int N { get; set; }

        private string value;

        [GlobalSetup]
        public void SetUp()
        {
            var chars = new char[N];
            for (int i = 0; i &amp;lt; N; i++)
            {
                chars[i] = (char)(i % 10 + '0');
            }
            
            value = new string(chars);
        }
        
        [Benchmark(Baseline = true)]
        public List&amp;lt;char&amp;gt; Constructor()
        {
            return new List&amp;lt;char&amp;gt;(value);
        }

        [Benchmark]
        public List&amp;lt;char&amp;gt; Constructor_ToCharArray()
        {
            return new List&amp;lt;char&amp;gt;(value.ToCharArray());
        }

        [Benchmark]
        public List&amp;lt;char&amp;gt; Constructor_ToArray()
        {
            return new List&amp;lt;char&amp;gt;(value.ToArray());
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;Full results in table format. Click to expand.&lt;/summary&gt;
&lt;pre&gt;&lt;code class="language-ini"&gt;BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19043.1288 (21H1/May2021Update)
AMD Ryzen 7 4700U with Radeon Graphics, 1 CPU, 8 logical and 8 physical cores
  [Host]             : .NET Framework 4.8 (4.8.4420.0), X64 RyuJIT
  .NET 5.0           : .NET 5.0.7 (5.0.721.25508), X64 RyuJIT
  .NET Core 3.1      : .NET Core 3.1.20 (CoreCLR 4.700.21.47003, CoreFX 4.700.21.47101), X64 RyuJIT
  .NET Framework 4.8 : .NET Framework 4.8 (4.8.4420.0), X64 RyuJIT

&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Runtime&lt;/th&gt;
&lt;th&gt;N&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Mean&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Error&lt;/th&gt;
&lt;th style="text-align: right;"&gt;StdDev&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Ratio&lt;/th&gt;
&lt;th style="text-align: right;"&gt;RatioSD&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Gen 0&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Allocated&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Constructor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;.NET 5.0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;115.76 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;1.517 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;1.345 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.74&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.01&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.0917&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;192 B&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constructor_ToCharArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;35.95 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.698 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.653 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.23&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0612&lt;/td&gt;
&lt;td style="text-align: right;"&gt;128 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constructor_ToArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;194.70 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.219 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.967 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.25&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.1299&lt;/td&gt;
&lt;td style="text-align: right;"&gt;272 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constructor&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;138.96 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.804 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.880 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.89&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0918&lt;/td&gt;
&lt;td style="text-align: right;"&gt;192 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constructor_ToCharArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;52.31 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.897 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.839 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.34&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0612&lt;/td&gt;
&lt;td style="text-align: right;"&gt;128 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constructor_ToArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;228.65 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.703 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.657 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.47&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.1299&lt;/td&gt;
&lt;td style="text-align: right;"&gt;272 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constructor&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;155.57 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.166 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.147 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0956&lt;/td&gt;
&lt;td style="text-align: right;"&gt;201 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constructor_ToCharArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;71.37 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.098 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.027 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.46&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.0650&lt;/td&gt;
&lt;td style="text-align: right;"&gt;136 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constructor_ToArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right;"&gt;232.85 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.850 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.730 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.50&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.1414&lt;/td&gt;
&lt;td style="text-align: right;"&gt;297 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Constructor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;.NET 5.0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;648.65 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;7.217 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;6.751 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.80&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.02&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.3405&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;712 B&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constructor_ToCharArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;51.29 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.921 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.817 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.06&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.2295&lt;/td&gt;
&lt;td style="text-align: right;"&gt;480 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constructor_ToArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;791.75 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;15.823 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;20.012 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.99&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.4663&lt;/td&gt;
&lt;td style="text-align: right;"&gt;976 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constructor&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;684.45 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.868 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.065 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.85&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.3405&lt;/td&gt;
&lt;td style="text-align: right;"&gt;712 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constructor_ToCharArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;70.10 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.527 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.493 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.09&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.2294&lt;/td&gt;
&lt;td style="text-align: right;"&gt;480 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constructor_ToArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;962.36 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.630 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.867 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.19&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.4654&lt;/td&gt;
&lt;td style="text-align: right;"&gt;976 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constructor&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;800.13 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;14.134 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;17.875 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.3443&lt;/td&gt;
&lt;td style="text-align: right;"&gt;722 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constructor_ToCharArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;95.32 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.880 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.931 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.12&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.2333&lt;/td&gt;
&lt;td style="text-align: right;"&gt;489 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constructor_ToArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td style="text-align: right;"&gt;960.90 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6.956 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5.431 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.19&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.03&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.5569&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,171 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Constructor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;.NET 5.0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1000&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;5,289.46 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;104.687 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;153.449 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.83&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;0.03&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;2.0828&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;4,368 B&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constructor_ToCharArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;235.86 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.589 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.507 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.9505&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4,080 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constructor_ToArray&lt;/td&gt;
&lt;td&gt;.NET 5.0&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5,943.57 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;111.670 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;204.195 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.95&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.1128&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6,528 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constructor&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;5,196.81 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;70.120 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;65.590 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.82&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.02&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.0828&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4,368 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constructor_ToCharArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;247.25 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.114 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.987 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.9503&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4,080 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constructor_ToArray&lt;/td&gt;
&lt;td&gt;.NET Core 3.1&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;7,100.14 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;85.990 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;80.435 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.13&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3.1128&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6,528 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constructor&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6,306.12 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;69.763 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;65.257 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.0905&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4,389 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constructor_ToCharArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;275.66 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.318 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.055 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.04&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.00&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.9541&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4,101 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Constructor_ToArray&lt;/td&gt;
&lt;td&gt;.NET Framework 4.8&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td style="text-align: right;"&gt;7,507.12 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;20.504 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;17.121 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.19&lt;/td&gt;
&lt;td style="text-align: right;"&gt;0.01&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4.0131&lt;/td&gt;
&lt;td style="text-align: right;"&gt;8,453 B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre&gt;&lt;code&gt;// * Legends *
  N         : Value of the 'N' parameter
  Mean      : Arithmetic mean of all measurements
  Error     : Half of 99.9% confidence interval
  StdDev    : Standard deviation of all measurements
  Ratio     : Mean of the ratio distribution ([Current]/[Baseline])
  RatioSD   : Standard deviation of the ratio distribution ([Current]/[Baseline])
  Gen 0     : GC Generation 0 collects per 1000 operations
  Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)
  1 us      : 1 Microsecond (0.000001 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;p&gt;Time diagram (in microseconds) for strings with 100 chars:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/collections/constructor-diagram-time.png" alt="Time diagram"&gt;&lt;/p&gt;
&lt;p&gt;Incredible! A list created from an array is about 10 times faster than created from a source string. And memory usage is more optimal (in bytes):&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.rogatnev.net/img/collections/constructor-diagram-time.png" alt="Memory diagram"&gt;&lt;/p&gt;
&lt;p&gt;But we should remember, it's a very specific case.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;In common, we should be more attentive and avoid unnecessary transformations for reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Performance. Such small optimizations can positively influence the processing of big data.&lt;/li&gt;
&lt;li&gt;Additional memory traffic — GC has to collect more data, GC pauses can be longer.&lt;/li&gt;
&lt;li&gt;Code quality. We can reduce code but not lose functionality.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some cases require special attention and a deeper understanding of the .net platform.&lt;/p&gt;
&lt;p&gt;If you really need to convert a string to an array use &lt;code&gt;ToCharArray()&lt;/code&gt; instead of &lt;code&gt;ToArray()&lt;/code&gt;.&lt;/p&gt;
&lt;h1 id="more-examples"&gt;More examples&lt;/h1&gt;
&lt;p&gt;Here are more examples I found.&lt;/p&gt;
&lt;h2 id="lowercase-string"&gt;Lowercase string&lt;/h2&gt;
&lt;p&gt;There is a very popular way to lowercase the first letter of a string. We remember — strings are immutable and &lt;code&gt;name[0] = char.ToLowerInvariant(name[0])&lt;/code&gt; won't compile. So, we need to create a new string from a char array.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public override string Format(string name)
{
	if (string.IsNullOrEmpty(name) || char.IsLower(name[0])) return name;
	var chars = name.ToCharArray();
	chars[0] = char.ToLowerInvariant(chars[0]);
	return new string(chars);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="reverse-string"&gt;Reverse string&lt;/h2&gt;
&lt;p&gt;There is no built-in method to reverse a string, so we have to create our own using &lt;code&gt;Array.Reverse&lt;/code&gt; method, so we transform a source string to an array.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public static string ReverseString(this string input)
{
	var charArray = input.ToCharArray();
	Array.Reverse(charArray);
	return new string(charArray);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="iterate-string"&gt;Iterate string&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public static byte[] ToBytes(string value)
{
	char[] chars = value.ToCharArray();
	byte[] res = new byte[chars.Length / 2];
	for (int i = 0, j = 0; i &amp;lt; chars.Length; i += 2, j++)
		res[j] = (byte)(FromHex((byte)chars[i]) &amp;lt;&amp;lt; 4 ^ FromHex((byte)chars[i + 1]));
	return res;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We iterate through a string, and no need to create a new array. The code can be simplified:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public static byte[] ToBytes(string value)
{
	byte[] res = new byte[value.Length / 2];
	for (int i = 0, j = 0; i &amp;lt; value.Length; i += 2, j++)
		res[j] = (byte)(FromHex((byte)value[i]) &amp;lt;&amp;lt; 4 ^ FromHex((byte)value[i + 1]));
	return res;
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded>
			<comments xmlns="http://purl.org/rss/1.0/modules/slash/">0</comments>
		</item>
	</channel>
</rss>