【问题标题】:Collection change security with yield return statements使用收益返回语句的集合更改安全性
【发布时间】:2013-12-03 19:51:36
【问题描述】:

使用 yield return 语句我总是想知道是否要实现版本计数:

private IEnumerator<string> GetStrings()
{
    int initialVersion = _version; // Where _version is incremented everytime the list changes
    foreach(string s in _listInternal)
    {
        if (initialVersion != _version)
            throw ...;
        yield return s; 
    }
}

foreach(string s in _listInternal) 当然会在这种情况下捕捉到这一点,但是如果我不使用集合或列表作为源,会有问题吗?

如果我更改创建枚举器的集合,它通常会使用此 _version 字段来保护自己在迭代器遍历项目时不被更改。当多个线程同时工作时,这也是一个问题。

【问题讨论】:

  • 我不明白你的问题。能否请您详细说明一下?
  • 这是一个线程安全问题吗?
  • @aj.toulan 可能,但通常不会。通常,它是一个单线程,它会改变 foreach 循环体中的底层集合,例如foreach(var item in list) list.Remove(item);
  • 我通常在这种情况下使用它。 list.RemoveAll(x => x.ShouldDelete);其中 ShouldDelete 是您正在操作的列表类型中的 bool 属性。我相信也可以这样写 list.RemoveAll(ShouldDelete);
  • @aj.toulan 是的,这是解决特定问题而不产生此错误的一种方法;我给出了一个编写不当的代码示例,如果使用该代码应该抛出此异常,期望调用者将其代码更改为更符合您所拥有的内容。我还证明了编写不当的代码不一定是(也可能不会是)多线程的。

标签: c# .net yield-return


【解决方案1】:

没有必要,假设_listInternal 实际上是List&lt;T&gt;List 枚举器已经实现了这个功能;如果您在迭代集合时对其进行了变异,其枚举器将引发异常,并且该异常将通过您的迭代器块向上传播。

如果您的迭代器块基于一个还没有此行为的基础集合,或者除了您使用的一些基础集合之外还有可变状态,但无法更改,那么您可能想要使用此模式,是的。

当然,为了能够实现这个模式,你需要有一些方法在底层数据改变时增加版本号。有时,如果您使用的是无法控制代码的集合,您就无法做到这一点。

【讨论】:

    【解决方案2】:

    不,你永远不需要使用一个版本来返回收益,它们应该被认为是原子的。

    【讨论】:

    • 当迭代器基于枚举的底层数据发生变化时,希望迭代器抛出异常是完全合适的。这是在许多地方使用的相当普遍的模式,说你永远不需要它根本不正确。
    • 正确,但我认为我所说的仍然有效。我把这个问题作为一个典型的用例。我不知道在可枚举块中抛出异常是常见的做法。我特别不希望从 get 方法中得到这一点。通常,当处理需要分布、分段或可枚举的长度不可知(例如无限集)时,会使用收益返回。我也没有看到作者在 foreach 中操纵集合。我认为这是一种不好的做法,因为您要在其内部更改 foreach 中的集合。 Linq 为这些问题提供了解决方案。
    • 您断言永远不应该这样做。这是完全不真实的。在某些情况下这样做是完全合适的,所以不,你所说的不应该成立。当您遇到阻止序列枚举的异常问题时,您应该从迭代器块中抛出异常;损坏的基础数据源肯定是合格的。产生不良数据而不是抛出会更糟。至于这里使用and iterator block;这只是一个例子;包括实际工作是不必要的,只会分散注意力。
    • “我认为这是一种不好的做法,因为您正在更改本身的 foreach 中的集合。”是的;这就是为什么如果你发现自己处于那个位置时应该抛出异常,作为一个迭代器,向开发人员表明他们做错了什么,而不是忽略问题并试图把它扫到地毯下。 “Linq 为这些问题提供了解决方案。”不,它没有。它会很高兴地让您在枚举它时修改底层集合。如果你想阻止你的迭代器的用户这样做,你需要自己做。
    • 我不确定你是否理解我的最后一部分,我认为现在已经无关紧要了。 “产生坏数据而不是扔掉会更糟。”是的,但是如果您的数据已损坏并且已获得收益回报,我不确定您是否遵循关注点分离。在访问之前应该有一个数据层。
    猜你喜欢
    • 2014-08-29
    • 1970-01-01
    • 1970-01-01
    • 2014-11-10
    • 2018-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多