当断点似乎神奇地在枚举器内的同一位置出现两次时,我花了一些时间挠头。
事实证明,这个错误是一个直接的疏忽:
protected override void Extract()
{
LogGettingOffers();
var offerIds = CakeMarketingUtility.OfferIds(advertiserId);
LogExtractingClicks(offerIds);
foreach (var offerId in offerIds)
{
int rowCount;
var clicks = RetryUtility.Retry(3, 10000, new[] { typeof(Exception) }, () =>
{
return CakeMarketingUtility.EnumerateClicks(dateRange, advertiserId, offerId);
});
foreach (var clickBatch in clicks.InBatches(1000))
{
LogExtractedClicks(offerId, clickBatch);
// SHOULD BE clickBatch, NOT clicks
Add(clicks);
}
}
End();
}
这让我想知道人们可能会采取什么(如果有的话)预防措施来编写捕获此类错误的代码。
注意,我不确定沿着这条思路走下去是否有意义 - 也许答案是“不要编写不正确的代码”,我愿意接受。
这是产生结果的实际代码:
public static IEnumerable<Click> EnumerateClicks(DateRange dateRange, int advertiserId, int offerId)
{
// initialize to start at the first row
int startAtRow = 1;
// hard code an upper limit for the max number of rows to be returned in one call
int rowLimitForOneCall = 5000;
bool done = false;
int total = 0;
while (!done)
{
Logger.Info("Extracted a total of {0} rows, checking for more, starting at row {1}..", total, startAtRow);
// prepare the request
var request = new ClicksRequest
{
start_date = dateRange.FromDate.ToString("MM/dd/yyyy"),
end_date = dateRange.ToDate.ToString("MM/dd/yyyy"),
advertiser_id = advertiserId,
offer_id = offerId,
row_limit = rowLimitForOneCall,
start_at_row = startAtRow
};
// create the client, call the service and check the response
var client = new ClicksClient();
var response = client.Clicks(request);
if (!response.Success)
{
throw new Exception("ClicksClient failed");
}
// update the running total
total += response.RowCount;
// return result
foreach (var click in response.Clicks)
yield return click;
// update stopping condition for loop
done = (response.RowCount < rowLimitForOneCall);
// increment start row for next iteration
startAtRow += rowLimitForOneCall;
}
Logger.Info("Extracted a total of {0}, done.", total);
}
最佳答案
对于这个具体问题,我想说的解决方案是“不要编写不正确的代码”。特别是当可以在不改变任何状态的情况下生成结果时(例如当您从列表中枚举元素时),我认为从任何可枚举创建多个枚举器应该没问题。
您可以创建一个 IEnumerable
包装器,以确保 GetEnumerator
仅被调用一次,但如果您实际上需要调用它两次怎么办?您真正想要的是捕获错误,而不是捕获多次枚举的可枚举值,而这不是您可以轻松放入软件解决方案中的东西。
问题可能在于 clickBatch
和 clicks
具有相同的类型,因此编译器无法区分两者。
关于c# - 防止意外重新枚举 IEnumerable 的技术?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17707671/