.NET 中有通用的(类型安全的)BitArray 吗?

发布于 2024-07-29 05:30:24 字数 499 浏览 10 评论 0 原文

.NET 中有通用的 BitArray 吗? 我只找到了非通用的。

可以有一个通用的 BitArray 吗? (即这合理吗?)


编辑:

也许我应该说类型安全而不是通用的。

基本上,当您将类型枚举为 object 时,它不应该是 intbool 吗? 或者在另一个成员枚举器中提供其中之一?


示例:

foreach (bool bit in myBitArray)
{

}

编辑:

我刚刚检查了 BitArray 类的枚举器,但除了 .Current 属性之外,所有内容都返回一个 object

public virtual object Current

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

送舟行 2024-08-05 05:30:24

BitArray 是 NET 1.x 时代的一个专门的集合类。 只要您使用 ba.Set(int, bool) 和索引器属性,它就是相当类型安全的。

“不类型安全”的是枚举,BitArray 实现了 IEnumerable 但不是 IEnumerable< 布尔>。 所以 Joan 是对的,使用 foreach() 涉及从对象到 bool 的转换。

但这是一个真正的问题吗? BitArray 中的元素是布尔值,只有与它们的位置组合时才有意义。 请注意,BitArray 没有 Add() 方法,只有 Set(i, true)

所以简单的答案是:不要使用 foreach() 或任何其他基于 IEnumerable 的东西。 它只会产生几乎没有用处的真/假值流。

在下面的代码片段中,BitArray 是完全类型安全且高效的:

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

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);
}
怪我闹别瞎闹 2024-08-05 05:30:24

不,不存在。

我什至不确定 BitArray 的哪一部分会是通用的(如果有的话)。

创建扩展并不难方法获取 BitArray 并使用 for 返回 bool[] List BitArray 上循环。 for 循环不会涉及装箱,因为您将使用 BitArray 的索引器和 bool[] List 也可以在不装箱的情况下进行枚举。

扩展方法示例:

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;
}

我从快速基准测试中发现(好奇心战胜了我),foreach (bool b in myBitArray.ToList()) 花费了 foreach 时间的 75% 到 85% (myBitArray 中的 bool b)。 这每次都会创建列表。 创建一次列表并对其进行多次迭代所花费的时间是 foreach (bool b in myBitArray) 所花费时间的 20% 到 25%。 仅当您需要多次迭代 bool 值并且知道它们自您调用 myBitArray 时起不会发生变化时,您才可以利用这一点.ToList()。

foreach (bool b in Enumerable.Cast 花费的时间是 foreach (bool b in myBitArray) 花费的时间的 150%。

另一个编辑:我想说,由于它是一款游戏,因此您可能会尽一切努力在没有装箱/拆箱的情况下进行非常精益的迭代,即使这意味着编写您自己的BitArray。 您可以节省时间并使用Reflector复制大部分内容 研究 BitArray 的代码,因为该类是密封的(无法继承和添加功能),以防万一有位操作优化可供学习。

编辑:提出从 Reflector 复制代码的建议。 有些东西,比如迭代器和闭包,会产生奇怪的生成代码,无论如何你都不想直接复制它们。

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.

起风了 2024-08-05 05:30:24

您可以迭代 BitArray,而无需装箱将其转换为 List

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

这应该比转换为列表更快,并且占用的内存肯定要少得多。

当然,它仍然比普通的旧 for 循环慢,如果您确实需要性能,则应该使用

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];
    }
}

结果(名称、迭代次数、总持续时间、得分(得分高则不好)):

============ 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

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
伪心 2024-08-05 05:30:24

如果存在的话,您将传递给 BitArray 的泛型类型参数的示例是什么?

定义 BitArray作为:

管理一个紧凑的位值数组,
表示为布尔值,
其中 true 表示该位是
on (1) 且 false 表示该位是
关闭(0)。

这种类型是一个优化的位数组,仅此而已。 使其通用没有任何价值,因为没有成员< /a> 可以从类型中分解出来。 任何像这样的专门集合都可以被视为某些父泛型集合的封闭构造类型。 换句话说,BitArray 有点像 List(当然添加了许多有用的方法)。

编辑:是的,此类型实现了IEnumerable,但没有实现IEnumerable - 这很可能是因为它是一个旧类型且未更新。 请记住,您可以使用 Enumerable.Cast 解决这个问题:

yourBitArray.Cast<bool>();

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>();
想挽留 2024-08-05 05:30:24

您选择通用版本的可能原因是什么? 除了位或根据情况转换为位的布尔值之外,BitArray 还可以使用什么类型?

更新:
它是类型安全的。 如果你正在执行 foreach(var bit in bitArray) 那么 bit 将显示为一个对象,但你也可以轻松地执行 foreach(bool bit in bitArray),这种情况发生在所有实现 IEnumerable 而不是 IEnumerableIEnumerable 的集合中。 T>.

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>.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文