迭代器中的副作用被认为是有害的?

时间:2008-11-26 11:34:30

标签: c# oop iterator

我今天写了第一个C#迭代器。哇噢。

有趣的是,它有副作用。我的迭代器从目录中过滤掉无效文件,并返回一系列要处理的有效文件。当它遇到一个invlaid文件时,它会将它移动到另一个目录。

我尝试将其实现为LINQ查询,但实际上并不喜欢where子句的谓词具有副作用的事实。那是一种确定的气味。

我可以明确地实现它,循环遍历所有文件并依次处理好坏,但它不是很优雅。更好的解决方案是将其拆分为两个列表(好的和坏的)并依次处理每个列表。

但后来我想起了迭代器。我现在有一个迭代器,它产生有效文件并处理(移动)无效文件。

所以,我的问题是:迭代器有这样的副作用是不是一个坏主意?我是否在迭代器中隐藏了太多功能?

9 个答案:

答案 0 :(得分:4)

有副作用的迭代器是不好的? :)

如果您的序列包含所有文件,则可以使用visitor - ish访问所有项目并为每种情况调用函数。访问者中的歧视可以是您可以提供的谓词,也可以是访问者内在的谓词。

所以,我不会说C#,而是像这样的伪代码:

good_handler = new FileHandler() {
  handle(File f) { print "Yay!"; }
}

bad_handler = new FileHandler() {
  handle(File f) { print "Nay!"; }
}

files = YourFileSequence();
visitor = new Visitor(good_handler, bad_handler);
visitor.visit(files);

答案 1 :(得分:3)

我说在迭代器中有副作用通常 一个坏主意,但它不是一个完整的禁忌。如果你有副作用,那么呼叫者很难/不可能以纯粹的功能方式工作。根据您的使用情况,这可能是也可能不是问题。

我建议你有两种获取迭代器的方法 - 一种具有副作用(基本上可能是一种优化),一种不具有(较慢,但更容易推理)。这可能只是通过将标志传递给方法,或者有两个名称不同的方法。

答案 2 :(得分:1)

逻辑上对集合进行枚举的迭代器不应该有副作用,不。特别是,当使用IEnumerator.Reset()方法重新启动时,它们将不是幂等的。

然而,迭代器实际上是一种协程,它们可用于实现一些在其他方面难以实现的东西,例如: steps in an asynchronous workflow

答案 3 :(得分:1)

另外一个问题是,该方法可能被“误用”,因为调用者可能会尝试使用它来移动文件,而不会对产生的结果感兴趣。

如果调用者从不迭代结果,则由于迭代器的延迟执行而不会调用(预期的)副作用。甚至可能存在用户仅迭代部分集合的情况,因此对某些项目执行副作用,但不是全部。“/ p>

这篇文章讨论了这个问题:http://codequota.com/archive/2012/02/13/iterator-blocks-and-side-effects.aspx

答案 4 :(得分:0)

我的经验法则是,如果我正在迭代一个集合,不。但是在Python中,for循环通常习惯性地用于执行代码一定次数,在这种情况下,使用副作用时我没有问题。

答案 5 :(得分:0)

谢谢大家 - 而且好笑!快速反应!

我必须同意迭代器中的副作用是个坏主意。我不得不问的事实表明有异味。应该听我的蜘蛛感。

我认为我提出的主要原因是因为我的副作用与主要任务完全隔离,因此整齐地封装在迭代器中。但是,它仍然是隐藏的功能,这不是很好。

另外,我认为我将访问者的想法与迭代器混为一谈,这也不是一个好主意。

我已经改变了我的实现,从我原来的所有文件序列生成2个序列 - 一个好,一个坏。我现在可以以更明显和直观的方式处理它们。万岁。

所以,我还没有在真实世界中使用迭代器。哦,好吧。

谢谢! 马特

答案 6 :(得分:0)

我会说副作用是一个坏主意但不是有害的。如果你有副作用,你基本上做两个操作。最好将这些操作分成两个函数,这样代码就更容易维护,你可以单独完成它们。

在这种情况下,您将坏文件从文件夹中移出,而将其他内容移动到好文件中。分离这些操作允许您移动坏文件而不选择好文件,或者让您操作好文件(例如计算它们)而不移动坏文件。您的代码也将更加划分,以便在需要时更容易优化其中一个操作。

答案 7 :(得分:0)

我认为实际上比你的迭代器有隐藏的副作用更直接。这是:你正在改变它正在迭代的集合的成员资格。即使副作用没有糟糕的代码味道,这也是你必须谨慎的事情。有一些方法你可以实现这个看似合理的方法(缓存文件列表并在重置迭代器时重用它,比如说)如果从集合中删除东西就会中断。

答案 8 :(得分:0)

我不会评论一般情况,但在你的情况下,我认为这是危险的。接口质量的一个好指标是正确使用接口是多么容易,以及错误地使用它有多难。

应用该指标,您的设计得分非常低,因为非常容易错误地使用它:只需迭代两次。

我实际上比Jon更进一步说:甚至不提供选项。它可能会有所帮助,但可能使用这个错误的价格可能太高了。另一方面,可以说,如果用户故意做出选择,他必须处理后果。