[API Proposal]: Add constructor BitArray(IEnumerable<bool> values)
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
### Background and motivation
I would like to propose adding a new constructor to `System.Collections.BitArray` that takes an `IEnumerable` as its parameter, similar to other collection types.
This would allow `BitArray` to be instantiated directly from an `IEnumerable`, improving usability and consistency with other collection classes.
### API Proposal
```diff
namespace System.Collections;
public sealed class BitArray
{
+ public BitArray(System.Collections.Generic.IEnumerable values)
}
```
### API Usage
```csharp
IEnumerable 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())
{
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.
```csharp
BitArray array;
IEnumerable enumerable = // something;
if (Enumerable.TryGetNonEnumeratedCount(enumerable, out int count))
{
array = new BitArray(count);
Span destination = CollectionsMarshal.AsBytes(array);
int index = -1;
foreach (bool sourceItem in enumerable)
{
++index;
if (sourceItem)
{
destination[index >>> 3] |= (byte)(1 << (index & 7));
}
}
}
else
{
List list = enumerable.ToList(); // x8 allocation
array = new BitArray(list.Count);
Span destination = CollectionsMarshal.AsBytes(array);
ReadOnlySpan 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`. This would reduce memory usage to one-eighth of the current requirement.
The above sample code is already sufficient for the constructors `BitArray(IEnumerable)` and `BitArray(IEnumerable)`, so adding dedicated constructors for these types would not be necessary.
#### the alternate API
```diff
namespace System.Collections;
public sealed class BitArray
{
+ public static BitArray Create(TSource values) where TSource : System.Collections.Generic.IEnumerable
}
```
This alternative API would have the advantage of avoiding boxing allocations when the `IEnumerable` argument comes from a value-type collection. However, such cases are extremely rare—Unity’s `NativeArray` 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_
Contributor guide
Assessment
This issue has not been assessed yet.