c# - 防止意外重新枚举 IEnumerable 的技术?

标签 c# enumerator

当断点似乎神奇地在枚举器内的同一位置出现两次时,我花了一些时间挠头。

事实证明,这个错误是一个直接的疏忽:

    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 仅被调用一次,但如果您实际上需要调用它两次怎么办?您真正想要的是捕获错误,而不是捕获多次枚举的可枚举值,而这不是您可以轻松放入软件解决方案中的东西。

问题可能在于 clickBatchclicks 具有相同的类型,因此编译器无法区分两者。

关于c# - 防止意外重新枚举 IEnumerable 的技术?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17707671/

相关文章:

c# - 为什么我无法旋转播放器变换?

Haskell 枚举器 : analog to iteratees `enumWith` operator?

Ruby:如何设置枚举器的状态?

c# - "Unzip"IEnumerable 在 C# 或最佳替代方案中动态

c# - 更新对象的通用方法错误( Entity Framework 4)

c# - ldobj 和 ldind.<type> 有什么区别,为什么 ldobj 更快?

c# - 今天、本周、本月流行——设计模式

c# - 将 2 个 foreach 循环减少到一个 linq 查询中

lazy-evaluation - 在 Crystal 中实现惰性枚举器

ruby - 重写 Ruby 注入(inject)