为什么通过集合枚举抛出一个异常,而是通过其项目循环不 [英] Why does enumerating through a collection throw an exception but looping through its items does not

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

问题描述

我测试了一些同步的结构,我注意到一些困惑我。当我在写它在同一时间通过枚举的集合,它抛出一个异常(这是预期),但是当我使用的是循环循环通过收集,事实并非如此。有人能解释一下吗?我认为,一个列表不会让读者和作家在同一时间运行。我本来期望通过收集循环表现出作为使用枚举相同的行为。

更新:这是一个纯粹的学术活动。我undersand该枚举列表是坏的,如果它正被写入的同时。我也明白,我需要一个同步结构。我的问题又是为什么操作之一抛出预期的异常,但其他没有。

code是如下:

 类节目
   {
    私有静态列表<字符串> _collection =新的名单,其中,串>();
    静态无效的主要(字串[] args)
    {
        ThreadPool.QueueUserWorkItem(新WaitCallback(为addItems),NULL);
        System.Threading.Thread.Sleep(5000);
        ThreadPool.QueueUserWorkItem(新WaitCallback(DisplayItems),NULL);
        到Console.ReadLine();
    }

    公共静态无效为addItems(对象STATE_)
    {
        的for(int i = 1; I< = 50;我++)
        {
            _collection.Add(i.ToString());
            Console.WriteLine(添加+ I);
            System.Threading.Thread.Sleep(150);
        }
    }

    公共静态无效DisplayItems(对象STATE_)
    {
        //这不会引发异常
        //的for(int i = 0; I< _collection.Count;我++)
        // {
        // Console.WriteLine(读书+ _collection [I]);
        // System.Threading.Thread.Sleep(150);
        //}

        //这将抛出一个异常
        名单<字符串> .Enumerator枚举= _collection.GetEnumerator();
        而(enumerator.MoveNext())
        {
            字符串值= enumerator.Current;
            System.Threading.Thread.Sleep(150);
            Console.WriteLine(读书+值);
        }
    }
}
 

解决方案

您不能修改的集合,同时列举了它。该规则的存在,即使没有线程考虑的问题。从<一个href="http://msdn.microsoft.com/en-us/library/system.collections.ienumerable.getenumerator.aspx">MSDN:

  

这是枚举仍然有效,只要集合保持不变。如果更改了收集,如添加,修改或删除元素,枚举数将失效且不可恢复,而且其行为是不确定的。

一个基于整数的循环实际上不是一个枚举。在大多数情况下是完成同样的事情。然而,对于IEnumerator的接口保证了您可以通过整个集合迭代。该平台通过抛出异常强制执行此内部如果后集合已被修改调用MoveNext的发生。此异常是由枚举器对象抛出。

基于整数的循环只有经过数字的名单。当您通过整数索引集合,你是刚刚起步的项目在该位置。如果事情已经插入或从列表中删除,你可以跳过一个项目或运行在同一项目两次。当你需要修改的集合,而穿越呢?这可能是在某些情况下非常有用。 for循环没有枚举器对象,以保证IEnumerator的合同,所以不会抛出异常。

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.

UPDATE: This is a purely academic exercise. I undersand that enumerating a list is bad if it is being written to at the same time. I also understand that I need a synchronization construct. My question again was about why operation one throws an exception as expected but the other does not.

Code is below:

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

解决方案

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.

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.

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天全站免登陆