-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Description
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