为什么通过集合枚举会引发异常但遍历其项目不会 [英] Why does enumerating through a collection throw an exception but looping through its items does not

查看:26
本文介绍了为什么通过集合枚举会引发异常但遍历其项目不会的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在测试一些同步结构,我注意到一些让我感到困惑的东西.当我枚举一个集合同时写入它时,它抛出了一个异常(这是预期的),但是当我使用 for 循环遍历集合时,它没有.有人可以解释一下吗?我认为 List 不允许读取器和写入器同时操作.我本来希望循环遍历集合会表现出与使用枚举器相同的行为.

I was testing out some synchronization constructs and I noticed something that confused me. When I was enumerating through a collection while writing to it at the same time, it threw an exception (this was expected), but when I looped through the collection using a for loop, it did not. Can someone explain this? I thought that a List does not allow a reader and writer to operate at the same time. I would have expected looping through the collection to exhibit the same behavior as using an enumerator.

更新:这是一个纯粹的学术练习.我知道如果同时写入列表,枚举列表是不好的.我也明白我需要一个同步构造.我的问题再次是关于为什么一个操作按预期抛出异常,而另一个却没有.

代码如下:

   class Program
   {
    private static List<string> _collection = new List<string>();
    static void Main(string[] args)
    {
        ThreadPool.QueueUserWorkItem(new WaitCallback(AddItems), null);
        System.Threading.Thread.Sleep(5000);
        ThreadPool.QueueUserWorkItem(new WaitCallback(DisplayItems), null);
        Console.ReadLine();
    }

    public static void AddItems(object state_)
    {
        for (int i = 1; i <= 50; i++)
        {
            _collection.Add(i.ToString());
            Console.WriteLine("Adding " + i);
            System.Threading.Thread.Sleep(150);
        }
    }

    public static void DisplayItems(object state_)
    {
        // This will not throw an exception
        //for (int i = 0; i < _collection.Count; i++)
        //{
        //    Console.WriteLine("Reading " + _collection[i]);
        //    System.Threading.Thread.Sleep(150);
        //}

        // This will throw an exception
        List<string>.Enumerator enumerator = _collection.GetEnumerator();
        while (enumerator.MoveNext())
        {
            string value = enumerator.Current;
            System.Threading.Thread.Sleep(150);
            Console.WriteLine("Reading " + value);
        }
    }
}

推荐答案

枚举时不能修改集合.即使没有考虑线程问题,该规则仍然存在.来自 MSDN:

You can't modify a collection while enumerating over it. That rule exists even without threading issues considered. From MSDN:

只要集合保持不变,枚举数就保持有效.如果对集合进行了更改,例如添加、修改或删除元素,则枚举器将不可恢复地失效,并且其行为未定义.

An enumerator remains valid as long as the collection remains unchanged. If changes are made to the collection, such as adding, modifying, or deleting elements, the enumerator is irrecoverably invalidated and its behavior is undefined.

基于整数的 for 循环实际上不是枚举器.在大多数情况下是完成同样的事情.但是,IEnumerator 的接口保证您可以遍历整个集合.如果在修改集合后发生对 MoveNext 的调用,则平台通过引发异常在内部强制执行此操作.这个异常是由枚举器对象抛出的.

A integer-based for loop is not actually an enumerator. In most scenarios is accomplishes the same thing. However, the interface for IEnumerator guarantees that you can iterate through the entire collection. The platform enforces this internally by throwing an exception if a call to MoveNext occurs after the collection has been modified. This exception is thrown by the enumerator object.

基于整数的 for 循环仅遍历其数字列表.当您按整数索引集合时,您只是在该位置获取项目.如果从列表中插入或删除了某些内容,您可以跳过一个项目或运行相同的项目两次.当您需要在遍历集合时修改集合时,这在某些情况下很有用.for 循环没有枚举器对象来保证 IEnumerator 契约,因此不会抛出异常.

The integer-based for loop only goes through its list of numbers. When you index the collection by integer, you are just getting the item at that position. If something has been inserted or deleted from the list, you may skip an item or run the same item twice. This can be useful in certain situations when you need to modify a collection while traversing it. The for loop has no enumerator object to guarantee the IEnumerator contract, therefore no exception is thrown.

这篇关于为什么通过集合枚举会引发异常但遍历其项目不会的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆