Skip to content

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

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions