将类添加到列表 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<T>
是线程安全的(静态方法除外),但您正在从多个线程对其进行修改。
这种类型的公共静态(在 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<T>
不是线程安全的,您从Parallel.For
内部调用.Add
。您要么需要锁定 Add
,要么使用 System.Collections.Concurrent
命名空间中的线程安全集合。
这不是您唯一遇到的线程错误,例如,在您的Parallel.For
中,您分配了几个变量,这些变量都在循环外的范围内声明。您的各个线程将在分配这些值时相互覆盖。
【讨论】:
这是我第一次使用并行for循环编写程序。那么解决这个问题的方法是在循环内声明变量?非常感谢您的建议。你还有其他人吗? 你有一大堆无用的.AsParallel()
代码只会减慢你的程序,例如你做amexList.AsParallel().Count()
,amexList
是一个List<StockData>
,只需调用@987654333 @ 它会给你数字,不需要计算(并行或其他)。
我什么时候应该使用 AsParallel 代码呢?我的印象是它加速了程序
并行代码有开销,当开销的成本小于你从并行获得的好处时,即你这样做。去阅读Patterns for Parallel Programming 这是一本非常好的微软介绍性电子书(免费),涵盖了这样的基本概念。
您还有什么建议吗?以上是关于将类添加到列表 C# 时,索引超出了数组的范围的主要内容,如果未能解决你的问题,请参考以下文章
C# - 在for循环中使用相同的列表大小,索引超出了数组的范围[重复]
删除最后一个数组元素时,SwiftUI列表ForEach索引超出范围