将类添加到列表 C# 时,索引超出了数组的范围

Posted

技术标签:

【中文标题】将类添加到列表 C# 时,索引超出了数组的范围【英文标题】:Index was outside the bounds of the array when adding class to list C# 【发布时间】:2015-04-03 10:35:37 【问题描述】:

异常:索引超出了数组的范围。

首先,我对这个异常很熟悉,并且我之前已经修复过它,但是我在代码中的一个非常奇怪的行中遇到了这个异常。当我将用户创建的类添加到代码中的类列表中时,它会被抛出。我完全不知道为什么会抛出这个异常以及如何修复它。

public static async Task getData()
    
        // initialize everything
        List<StockData> stockData = new List<StockData>();
        List<StockMarketCompare> stockCompareData = new List<StockMarketCompare>();
        List<StockData> sandpInfo = new List<StockData>();
        List<StockData> sandpDateInfo = new List<StockData>();
        List<StockData> amexList = new List<StockData>();
        List<DateTime> completedDates = new List<DateTime>();
        SymbolInfo symbolClass = new SymbolInfo();
        List<SymbolInfo> ratingSymbols = new List<SymbolInfo>();
        List<StockRating> ratingList = new List<StockRating>();
        bool isGoodToGo = false;
        string symbol, market;
        int activeSymbolsCount = 0;
        int rowCount = 0, completedRowCount = 0;
        DateTime date = new DateTime();
        DateTime searchDate = new DateTime();

        using (SqlConnection connection = new SqlConnection("connectionstring"))
        using (SqlCommand sandpCommand = new SqlCommand("select * from dbo.DailyGlobalData where Symbol='" + Calculations.sp500 + "'", connection))
        using (SqlDataAdapter sandpAdapter = new SqlDataAdapter(sandpCommand))
        using (DataTable sandpTable = new DataTable("sandp"))
        using (SqlCommand stockRatingsCommand = new SqlCommand("select * from dbo.StockRatings", connection))
        using (SqlDataAdapter stockRatingsAdapter = new SqlDataAdapter(stockRatingsCommand))
        using (DataTable stockRatingsTable = new DataTable("stockratings"))
        
            try
            
                // fill the sandptable
                sandpAdapter.Fill(sandpTable);

                if (sandpTable != null)
                
                    var sandpQuery = from c in sandpTable.AsEnumerable()
                                     select new StockData  Close = c.Field<decimal>("Close"), Date = c.Field<DateTime>("Date"), High = c.Field<decimal>("High"), Low = c.Field<decimal>("Low"), Volume = c.Field<Int64>("Volume") ;
                    sandpInfo = sandpQuery.AsParallel().ToList();
                

                // fill the stockratingstable
                stockRatingsAdapter.Fill(stockRatingsTable);

                if (stockRatingsTable != null)
                
                    activeSymbolsCount = stockRatingsTable.Rows.Count;

                    var symbolsAmountQuery = from c in stockRatingsTable.AsEnumerable()
                                             select new SymbolInfo  Symbol = c.Field<string>("Symbol"), Market = c.Field<string>("Market") ;
                    ratingSymbols = symbolsAmountQuery.AsParallel().ToList();
                

                for (int i = 0; i < activeSymbolsCount; i++)
                
                    symbol = ratingSymbols.AsParallel().ElementAtOrDefault(i).Symbol;
                    market = ratingSymbols.AsParallel().ElementAtOrDefault(i).Market;
                    ratingList = new List<StockRating>();

                    using (SqlCommand historicalRatingsCommand = new SqlCommand("select * from dbo.OldStockRatings where Symbol='" + symbol + "' and Market='" + market + "'", connection))
                    using (SqlDataAdapter historicalRatingsAdapter = new SqlDataAdapter(historicalRatingsCommand))
                    using (DataTable historicalRatingsTable = new DataTable("historicalratings"))
                    
                        // fill the historical ratings table
                        historicalRatingsAdapter.Fill(historicalRatingsTable);

                        if (historicalRatingsTable != null)
                        
                            completedRowCount = historicalRatingsTable.AsEnumerable().AsParallel().Count();
                            completedDates = historicalRatingsTable.AsEnumerable().AsParallel().Select(d => d.Field<DateTime>("Date")).ToList();
                        
                    
                            using (SqlCommand amexCommand = new SqlCommand("select * from dbo.DailyAmexData where Symbol='" + symbol + "'", connection))
                            using (SqlDataAdapter amexAdapter = new SqlDataAdapter(amexCommand))
                            using (DataTable amexTable = new DataTable("amexdata"))
                            
                                // fill the amex data table
                                amexAdapter.Fill(amexTable);

                                if (amexTable != null)
                                
                                    var amexFillQuery = from c in amexTable.AsEnumerable()
                                                        select new StockData  Close = c.Field<decimal>("Close"), Date = c.Field<DateTime>("Date"), High = c.Field<decimal>("High"), Low = c.Field<decimal>("Low"), Volume = c.Field<Int64>("Volume") ;
                                    amexList = amexFillQuery.AsParallel().ToList();

                                    rowCount = amexList.AsParallel().Count();
                                
                            

                    Parallel.For(0, rowCount - 30, new ParallelOptions
                    
                        MaxDegreeOfParallelism = Environment.ProcessorCount
                    , async j =>
                    
                                if (amexList.AsParallel().Count() > 0)
                                
                                    date = amexList.AsParallel().ElementAtOrDefault(j).Date;
                                    searchDate = date.Subtract(TimeSpan.FromDays(60));

                                    if (completedDates.Contains(date) == false)
                                    
                                        var amexQuery = from c in sandpInfo
                                                        where c.Date >= searchDate && c.Date <= date
                                                        join d in amexList on c.Date equals d.Date
                                                        select new StockMarketCompare  stockClose = d.Close, marketClose = c.Close ;

                                        var amexStockDataQuery = from c in amexList
                                                                 where c.Date >= searchDate && c.Date <= date
                                                                 select new StockData  Close = c.Close, High = c.High, Low = c.Low, Volume = c.Volume, Date = c.Date ;

                                        stockCompareData = amexQuery.AsParallel().ToList();
                                        stockData = amexStockDataQuery.AsParallel().ToList();
                                        isGoodToGo = true;
                                    
                                    else
                                    
                                        isGoodToGo = false;
                                    
                                

                        if (completedDates.Contains(date) == false)
                        
                            var sandpDateQuery = from c in sandpInfo
                                                 where c.Date >= searchDate && c.Date <= date
                                                 select c;
                            sandpDateInfo = sandpDateQuery.AsParallel().ToList();
                            symbolClass = new SymbolInfo()  Symbol = symbol, Market = market ;
                            isGoodToGo = true;
                        
                        else
                        
                            isGoodToGo = false;
                        

                        if (isGoodToGo)
                        
                            StockRating rating = performCalculations(symbolClass, date, sandpInfo, stockData, stockCompareData);

                                if (rating != null)
                                
                                    **ratingList.Add(rating);** // getting the exception thrown here
                                
                        
                    );

                    // now save the results to the table outside the parallel for loop

                    ratingList.RemoveAll(item => item == null);
                    List<StockRating> masterList = ratingList.DistinctBy(j => j.date).ToList();
                    saveToTable(masterList, symbol, market);
                    // close the connection
                    connection.Close();
                
            
            catch (Exception ex)
            
                Console.WriteLine(ex.Message);
            
            finally
            
                // close the connection
                connection.Close();
            
        
    

【问题讨论】:

Questions seeking debugging help ("why isn't this code working?") must include the desired behavior, a specific problem or error and the shortest code necessary to reproduce it in the question itself. Questions without a clear problem statement are not useful to other readers 您能否将此代码减少到可以重现错误的绝对最小值? @DerekTomes 我尽可能地减少了代码,所以我希望现在更好 【参考方案1】:

ratingsList 不是线程安全的,因为不能保证 List&lt;T&gt; 是线程安全的(静态方法除外),但您正在从多个线程对其进行修改。

这种类型的公共静态(在 Visual Basic 中为共享)成员是线程安全的。不保证任何实例成员都是线程安全的。 对 List 执行多次读取操作是安全的,但如果在读取时修改集合,则会出现问题。为确保线程安全,请在读取或写入操作期间锁定集合。要使一个集合能够被多个线程访问以进行读写,您必须实现自己的同步。对于具有内置同步的集合,请参阅 System.Collections.Concurrent 命名空间中的类。有关固有的线程安全替代方案,请参阅 ImmutableList 类。

https://msdn.microsoft.com/en-us/library/6sh2ey19%28v=vs.110%29.aspx

请改用thread safe collection。

【讨论】:

【参考方案2】:

List&lt;T&gt; 不是线程安全的,您从Parallel.For 内部调用.Add。您要么需要锁定 Add,要么使用 System.Collections.Concurrent 命名空间中的线程安全集合。

这不是您唯一遇到的线程错误,例如,在您的Parallel.For 中,您分配了几个变量,这些变量都在循环外的范围内声明。您的各个线程将在分配这些值时相互覆盖。

【讨论】:

这是我第一次使用并行for循环编写程序。那么解决这个问题的方法是在循环内声明变量?非常感谢您的建议。你还有其他人吗? 你有一大堆无用的.AsParallel() 代码只会减慢你的程序,例如你做amexList.AsParallel().Count()amexList 是一个List&lt;StockData&gt;,只需调用@987654333 @ 它会给你数字,不需要计算(并行或其他)。 我什么时候应该使用 AsParallel 代码呢?我的印象是它加速了程序 并行代码有开销,当开销的成本小于你从并行获得的好处时,即你这样做。去阅读Patterns for Parallel Programming 这是一本非常好的微软介绍性电子书(免费),涵盖了这样的基本概念。 您还有什么建议吗?

以上是关于将类添加到列表 C# 时,索引超出了数组的范围的主要内容,如果未能解决你的问题,请参考以下文章

C# - 在for循环中使用相同的列表大小,索引超出了数组的范围[重复]

索引超出数组范围:杂货项目列表添加

删除最后一个数组元素时,SwiftUI列表ForEach索引超出范围

Networkx:通过循环遍历节点列表添加属性时,IndexError:列表索引超出范围

Unity C# - 数组索引超出范围

Swift 致命错误:数组索引超出范围