Skip to content

[API Proposal]: Add constructor BitArray(IEnumerable<bool> values) #121737

@pCYSl5EDgo

Description

@pCYSl5EDgo

Background and motivation

I would like to propose adding a new constructor to System.Collections.BitArray that takes an IEnumerable<bool> as its parameter, similar to other collection types.
This would allow BitArray to be instantiated directly from an IEnumerable<bool>, improving usability and consistency with other collection classes.

API Proposal

namespace System.Collections;

public sealed class BitArray
{
+ public BitArray(System.Collections.Generic.IEnumerable<bool> values)
}

API Usage

IEnumerable<bool> enumerable = Enumerable.Range(0, 100).Where(static x => x % 31 == 0).Select(static x => (x & 1) == 0);
BitArray array = new(enumerable);
foreach (bool item in array.OfType<bool>())
{
    Console.WriteLine(item ? "event" : "odd");
}

Alternative Designs

If this constructor is not available, the following sample code would likely be the most optimal way to initialize a BitArray:

The code sample without `BitArray` with `IEnumerable` parameter.
BitArray array;
IEnumerable<bool> enumerable = // something;
if (Enumerable.TryGetNonEnumeratedCount(enumerable, out int count))
{
    array = new BitArray(count);
    Span<byte> destination = CollectionsMarshal.AsBytes(array);
    int index = -1;
    foreach (bool sourceItem in enumerable)
    {
        ++index;
        if (sourceItem)
        {
            destination[index >>> 3] |= (byte)(1 << (index & 7));
        }
    }
}
else
{
    List<bool> list = enumerable.ToList(); // x8 allocation
    array = new BitArray(list.Count);
    Span<byte> destination = CollectionsMarshal.AsBytes(array);
    ReadOnlySpan<bool> source = CollectionsMarshal.AsSpan(list);
    for (int index = 0; index < source.Length; index++)
    {
        if (source[index])
        {
            destination[index >>> 3] |= (byte)(1 << (index & 7));
        }
    }
}

With this constructor available, even if Enumerable.TryGetNonEnumeratedCount returns false and enumeration is required to determine the count, the temporary expansion buffer could be a bit-packed list instead of a List<bool>. This would reduce memory usage to one-eighth of the current requirement.

The above sample code is already sufficient for the constructors BitArray(IEnumerable<int>) and BitArray(IEnumerable<byte>), so adding dedicated constructors for these types would not be necessary.

the alternate API

namespace System.Collections;

public sealed class BitArray
{
+ public static BitArray Create<TSource>(TSource values) where TSource : System.Collections.Generic.IEnumerable<bool>
}

This alternative API would have the advantage of avoiding boxing allocations when the IEnumerable<bool> argument comes from a value-type collection. However, such cases are extremely rare—Unity’s NativeArray<bool> is probably the only notable example. Given this, I don’t think it’s necessary to optimize the API to that extent, and a straightforward constructor would be perfectly suitable.

Risks

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-suggestionEarly API idea and discussion, it is NOT ready for implementationarea-System.CollectionsuntriagedNew issue has not been triaged by the area owner

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions