使用LINQ where foreach:隐藏if语句,双foreach?

时间:2017-07-04 15:22:10

标签: c# linq foreach

foreach (Person criminal in people.Where(person => person.isCriminal)
{
    // do something
}

我有这段代码,想知道它是如何工作的。它是否等同于嵌套在foreach迭代中的if语句,还是首先循环遍历人员列表并使用选定的值重复循环?我希望从效率的角度更多地了解这一点。

foreach (Person criminal in people)
{
    if (criminal.isCriminal)
    {
        // do something
    }
}

3 个答案:

答案 0 :(得分:3)

Where使用延迟执行。

这意味着当您致电Where时,不会立即进行过滤。相反,每次在GetEnumerator().MoveNext()的返回值上调用Where时,它都会检查序列中的下一个元素是否满足条件。如果没有,它会跳过此元素并检查下一个元素。如果有一个元素满足条件,它会停止前进,您可以使用Current获取值。

基本上,就像在foreach循环中使用if语句一样。

答案 1 :(得分:1)

要了解会发生什么,您必须知道IEnumerables<T>如何工作(因为LINQ to Objects始终在IEnumerables<T>上工作。IEnumerables<T>返回一个实现迭代器的IEnumerator<T>。迭代器是惰性的,即它总是只生成序列中的一个元素。除非你有一个OrderBy或另一个需要它的命令,否则不会提前完成循环。

所以,如果你有......

foreach (string name in source.Where(x => x.IsChecked).Select(x => x.Name)) {
    Console.WriteLine(name);
}

......会发生这种情况:foreach语句需要Select请求的第一个项目,而Where需要一个来自Select的项目,而该项目又会从Where中检索一个项目。资源。第一个名称将打印到控制台。

然后foreach语句需要import sys import cv2 from PyQt5.QtCore import QThread, pyqtSignal, Qt from PyQt5.QtGui import QPixmap, QImage from PyQt5.QtWidgets import QApplication, QWidget, QLabel import datetime class App(QWidget): def __init__(self): super().__init__() self.title = 'PyQt5 Video' self.left = 100 self.top = 100 self.width = 640 self.height = 480 self.initUI() def initUI(self): self.setWindowTitle(self.title) self.setGeometry(self.left, self.top, self.width, self.height) self.resize(1800, 1200) # create a label label = QLabel(self) label.move(280, 120) label.resize(640, 480) label1 = QLabel(self) label1.move(680, 820) th = Thread(self) th.changePixmap.connect(lambda p: label.setPixmap(p)) th.changeLabel.connect(lambda n:label1.setText("A")) th.start() class Thread(QThread): changePixmap = pyqtSignal(QPixmap) changeLabel = pyqtSignal(QLabel) def __init__(self, parent=None): QThread.__init__(self, parent=parent) def run(self): cap = cv2.VideoCapture(0) n=QLabel() while True: ret, frame = cap.read() rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) convertToQtFormat = QImage(rgbImage.data, rgbImage.shape[1], rgbImage.shape[0], QImage.Format_RGB888) convertToQtFormat = QPixmap.fromImage(convertToQtFormat) p = convertToQtFormat.scaled(640, 480, Qt.KeepAspectRatio) self.changePixmap.emit(p) now = datetime.datetime.now() sec = now.second try: self.changeLabel.emit(n) except Exception as e: print(str(e)) if __name__ == '__main__': app = QApplication(sys.argv) ex = App() ex.show() sys.exit(app.exec_()) 请求的第二个项目,而(nSpend / 365)则需要Function formula_test(dtCurrentDate As Date, dtEffectiveDate As Date, nDays As Long, nSpend As Double) As Double On Error GoTo eHandle If dtEffectiveDate > dtCurrentDate Then formula_test = 0 Else If ((dtCurrentDate - dtEffectiveDate) + 1) > nDays Then formula_test = nDays * (nSpend / 365) Else formula_test = ((nSpend / 365) * (dtCurrentDate - dtEffectiveDate + 1)) If (dtCurrentDate - dtCurrentDate + 1) - nDays > 0 Then formula_test = formula_test - nSpend / 365 End If End If End If Exit Function eHandle: formula_test = 0 End Function 中的一个项目,而该项目又从源中检索一个项目。第二个名称将打印到控制台。

等等。

这意味着您的两个代码片段在逻辑上都是等效的。

答案 2 :(得分:0)

这取决于people是什么。

如果peopleIEnumerable对象(如集合,或使用yield的方法的结果),那么您问题中的两段代码确实是等效的。

天真的Where可以实现为:

public static IEnumerable<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
  // Error handling left out for simplicity.
  foreach (TSource item in source)
  {
    if (predicate(item))
    {
      yield return item;
    }
  }
}

Enumerable中的实际代码有点不同,以确保传递空sourcepredicate的错误立即发生而不是延迟执行,并优化少数情况(例如source.Where(x => x.IsCriminal).Where(x => x.IsOnParole)变为source.Where(x => x.IsCriminal && x.IsOnParole)的等价物,因此迭代链中的步骤少一个),但这是基本原则。

但是,如果peopleIQueryable,那么事情会有所不同,并且取决于相关查询提供程序的详细信息。

最简单的可能性是查询提供程序无法对Where执行任何特殊操作,因此它最终只执行上述操作,因为它仍然有用。

但查询提供商通常可以做其他事情。假设people是实体框架中的DbSet<Person>,与名为people的数据库中的表相关联。如果你这样做:

foreach(var person in people)
{
  DoSomething(person);
}

然后Entity Framework将运行类似于SQL的SQL:

SELECT *
FROM people

然后为返回的每一行创建一个Person对象。我们可以在即将实施Where时进行相同的过滤,但我们也可以做得更好。

如果你这样做:

foreach (Person criminal in people.Where(person => person.isCriminal)
{
  DoSomething(person);
}

然后Entity Framework将运行类似于SQL的SQL:

SELECT *
FROM people
WHERE isCriminal = 1

这意味着决定返回哪些元素的逻辑是在数据库返回.NET之前完成的。它允许在计算WHERE时使用索引,这可以更有效,但即使在没有有用索引的情况下,数据库必须进行全面扫描,它仍然意味着那些记录我们不关心从来没有从数据库中报告过,并且没有为它们创建的对象只是为了再次丢弃,所以性能的差异可能是巨大的。

  

我希望从效率的角度了解更多信息

您可能会感到满意,因为您建议可能会发生双重传递,并且很高兴知道它比您建议的foreach … if更有效。

foreachif仍然会.Where()IEnumerable(但不会针对数据库来源),因为{{1}会有一些开销} Whereforeach没有,但它只是在一个非常热门的路径中值得关注的程度。通常if可以合理放心地使用Where