Is there a generic (type-safe) BitArray in .NET?

我的梦境 提交于 2019-12-22 07:51:25

问题


Is there a generic BitArray in .NET? I only found the non-generic one.

Can there be a generic BitArray? (i.e. would it be reasonable?)


Edit:

Maybe I should have said type-safe not generic.

Basically when you enumerate the type as object, should it not be int or bool? Or one of them provided in another member enumerator?


Example:

foreach (bool bit in myBitArray)
{

}

Edit:

I just checked the enumerator of the BitArray class, but everything returns an object except .Current property:

public virtual object Current

回答1:


No, there isn't.

I'm not even sure what part of a BitArray would be generic if there were one.

It wouldn't be hard to create an extension method to take the BitArray and return a bool[] or List<bool> using a for loop on the BitArray. The for loop would not involve boxing since you would be using the BitArray's indexer, and the bool[] List<bool> could be enumerated without boxing as well.

Example extension method:

static List<bool> ToList( this BitArray ba ) {
    List<bool> l = new List<bool>(ba.Count);
    for ( int i = 0 ; i < ba.Count ; i++ ) {
        l.Add( ba[ i ] );
    }
    return l;
}

What I found from a quick benchmark (curiosity overcame me) was that foreach (bool b in myBitArray.ToList()) took 75% to 85% of the time that foreach (bool b in myBitArray). That creates the list each time. Creating the list once and iterating over it many times took 20% to 25% of the time that foreach (bool b in myBitArray) took. You could only take advantage of that if you need to iterate over the bool values multiple times and know that they won't have changed from the time you called myBitArray.ToList().

foreach (bool b in Enumerable.Cast<bool(myBitArray)) took 150% of the time that foreach (bool b in myBitArray) took.

Yet another edit: I'd say that since it is a game, it probably does make sense for you to do whatever it takes to have a very lean iteration with no boxing/unboxing, even if that means writing your own BitArray. You could save time and use Reflector to copy most of study BitArray's code since the class is sealed (can't inherit and add functionality), just in case there are bit-twiddling optimizations to learn from.

Edit: Struck the suggestion to copy code out of Reflector. Some things, like iterators and closures, yield weird generated code that you don't want to copy directly anyway.




回答2:


BitArray is a specialized collection class from the NET 1.x era. It is quite type-safe as long as you use ba.Set(int, bool) and the indexer property.

What is 'not typesafe' is the enumeration, BitArray implements IEnumerable but not IEnumerable< bool>. So Joan is right, using foreach() involves casting from object to bool.

But is that a real problem? The elements in a BitArray are booleans, and only meaningful when combined with their position. Note that BitArray does not have an Add() method, just a Set(i, true).

So the simple answer is: don't use foreach(), or anything else based on IEnumerable. It only produces a stream of true/false values that can hardly be useful.

In the following snippet the BitArray is perfectly type-safe and efficient:

BitArray isEven = ...;
for(int i = 0; i < isEven.Count; i++) 
{
   isEven.Set(i, i % 2 == 0);
}



回答3:


You can iterate BitArray without boxing or converting it to List<bool>:

public static IEnumerable<bool> GetTypeSafeEnumerator(this BitArray ba) {
    for (int i = 0; i < ba.Length; i++)
        yield return ba[i];
}

This should be faster than converting to list and certainly take much less memory.

Of course, it is still going to be slower than a plain old for loop, and if you really need performance, you should use

for (int i = 0; i < ba.Length; i++) {
    bool b = ba[i];
    ...
}

Benchmark using MiniBench:

public static class Class1 {
    private const int N = 10000;
    private const int M = 100;

    public static void Main() {
        var bitArray = new BitArray(N);

        var results1 = new TestSuite<BitArray, int>(
            "Different looping methods")
            .Plus(PlainFor, "Plain for loop")
            .Plus(ForEachBool, "foreach(bool bit in bitArray)")
            .Plus(CastBool, "foreach(bool bit in bitArray.Cast<bool>)")
            .Plus(TypeSafeEnumerator, "foreach(bool bit in bitArray.GetTypeSafeEnumerator())")
            .Plus(UseToList, "foreach(bool bit in bitArray.ToList())")
            .RunTests(bitArray, 0);

        results1.Display(ResultColumns.All, results1.FindBest());

        var results2 = new TestSuite<BitArray, int>(
            "Avoiding repeated conversions")
            .Plus(PlainFor1, "Plain for loop")
            .Plus(CastBool1, "foreach(bool bit in bitArray.Cast<bool>)")
            .Plus(TypeSafeEnumerator1, "foreach(bool bit in bitArray.GetTypeSafeEnumerator())")
            .Plus(UseToList1, "foreach(bool bit in bitArray.ToList())")
            .RunTests(bitArray, 0);

        results2.Display(ResultColumns.All, results2.FindBest());
    }

    private static int PlainFor1(BitArray arg) {
        int j = 0;
        for (int k = 0; k < M; k++) {
            for (int i = 0; i < arg.Length; i++) {
                j += arg[i] ? 1 : 0;
            }
        }
        return j;
    }

    private static int CastBool1(BitArray arg) {
        int j = 0;
        var ba = arg.Cast<bool>();
        for (int k = 0; k < M; k++) {
            foreach (bool b in ba) {
                j += b ? 1 : 0;
            }
        }
        return j;
    }

    private static int TypeSafeEnumerator1(BitArray arg) {
        int j = 0;
        var ba = arg.GetTypeSafeEnumerator();
        for (int k = 0; k < M; k++) {
            foreach (bool b in ba) {
                j += b ? 1 : 0;
            }
        }
        return j;
    }

    private static int UseToList1(BitArray arg) {
        int j = 0;
        var ba = arg.ToList();
        for (int k = 0; k < M; k++) {
            foreach (bool b in ba) {
                j += b ? 1 : 0;
            }
        }
        return j;
    }

    private static int PlainFor(BitArray arg) {
        int j = 0;
        for (int i = 0; i < arg.Length; i++) {
            j += arg[i] ? 1 : 0;
        }
        return j;
    }

    private static int ForEachBool(BitArray arg) {
        int j = 0;
        foreach (bool b in arg) {
            j += b ? 1 : 0;                
        }
        return j;
    }

    private static int CastBool(BitArray arg) {
        int j = 0;
        foreach (bool b in arg.Cast<bool>()) {
            j += b ? 1 : 0;
        }
        return j;
    }

    private static int TypeSafeEnumerator(BitArray arg) {
        int j = 0;
        foreach (bool b in arg.GetTypeSafeEnumerator()) {
            j += b ? 1 : 0;
        }
        return j;
    }

    private static int UseToList(BitArray arg) {
        int j = 0;
        foreach (bool b in arg.ToList()) {
            j += b ? 1 : 0;
        }
        return j;
    }

    public static List<bool> ToList(this BitArray ba) {
        List<bool> l = new List<bool>(ba.Count);
        for (int i = 0; i < ba.Count; i++) {
            l.Add(ba[i]);
        }
        return l;
    }

    public static IEnumerable<bool> GetTypeSafeEnumerator(this BitArray ba) {
        for (int i = 0; i < ba.Length; i++)
            yield return ba[i];
    }
}

Results (name, number of iterations, total duration, score (high score is bad)):

============ Different looping methods ============
Plain for loop                                        456899 0:28.087 1,00
foreach(bool bit in bitArray)                         135799 0:29.188 3,50
foreach(bool bit in bitArray.Cast<bool>)               81948 0:33.183 6,59
foreach(bool bit in bitArray.GetTypeSafeEnumerator()) 179956 0:27.508 2,49
foreach(bool bit in bitArray.ToList())                161883 0:27.793 2,79

============ Avoiding repeated conversions ============
Plain for loop                                        5381 0:33.247 1,00
foreach(bool bit in bitArray.Cast<bool>)               745 0:28.273 6,14
foreach(bool bit in bitArray.GetTypeSafeEnumerator()) 2304 0:27.457 1,93
foreach(bool bit in bitArray.ToList())                4603 0:30.583 1,08



回答4:


What would be an example of a generic type argument that you would pass to BitArray<T> if it existed?

BitArray is defined as:

Manages a compact array of bit values, which are represented as Booleans, where true indicates that the bit is on (1) and false indicates the bit is off (0).

This type is an optimized array of bits, nothing else. There is no value to making it generic as there are no members that could be factored out of the type. Any specialized collection like this one can be though of as a closed constructed type of some parent generic collection. In other words, BitArray is kind of like List<Boolean> (with many useful methods added of course).

Edit: Yes, this type implements IEnumerable and does not implement IEnumerable<T> - this is most likely due to the fact that it is an older type and was not updated. Remember that you can use Enumerable.Cast<TResult> to work around this very problem:

yourBitArray.Cast<bool>();



回答5:


What possible reason would you have for a generic version? What type could a BitArray possibly use beside bits, or booleans which are turned into bits as the the case is?

Updated: It is type safe. If you are doing a foreach(var bit in bitArray) then bit will appear as an object, but you can just as easily do foreach(bool bit in bitArray), this happens for all collections that implement IEnumerable and not IEnumerable<T>.



来源:https://stackoverflow.com/questions/1213997/is-there-a-generic-type-safe-bitarray-in-net

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!