具有嵌套foreach循环的编译器效率如何?

时间:2014-07-01 15:06:51

标签: c# .net

如果我有一些foreach循环,例如

foreach (ObjectA a in getAs(param1))
{
    foreach (ObjectB b in getBs(param2))
    {
        //compare a & b
    }
}

假设我没有对返回的列表进行任何编辑(它们返回相同的列表),编译器是否足够聪明,只能调用getAs()& getBs()一次,每次?它是否足够聪明,知道它应该将它们转换成字典?

它最终是否会通过LNQ与SQL数据库进行交互,这会产生重大影响(例如,代码可能假设SQL数据库随时都可以更改,但也许它可以检查它是否可以&# 39; s改变了?)

2 个答案:

答案 0 :(得分:2)

简短的回答是 ,但是长答案更多地与编写代码的方式有关,而不是编译器的效率。

在你的第一个foreach循环中,你获取源一次,并迭代它的元素,有效地只调用一次方法以获得源,但是嵌套的foreach循环被调用一次外循环中的每次迭代,并且由于编译器无法知道嵌套循环中方法的输出是否不会发生变化,因此总是调用方法以获取源是有意义的迭代。

在这种情况下,如果您知道输出将是相同的,那么您需要改进逻辑并避免对方法进行不必要的调用,特别是如果您知道有一些资源消耗操作(例如对数据库的调用)正在进行在那个方法里面。

为了给出详细的答案,我在以下程序中尝试了你的陈述:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace StackOverflow
{
    public class Class1
    {
        public static void Main(string[] args)
        {
            var serv1 = new Testserv1();
            foreach (int x in serv1.CallA())
            {
                foreach (int y in serv1.CallB())
                {
                }
            }
            Console.WriteLine(string.Format("Calls to A: {0}", serv1.ACount));
            Console.WriteLine(string.Format("Calls to B: {0}", serv1.BCount));
            Console.ReadLine();
        }

    }

    public class Testserv1
    {
        private static int CallACount = 0;
        private static int CallBCount = 0;

        public int ACount {
            get
            {
                return CallACount;
            }
        }

        public int BCount
        {
            get
            {
                return CallBCount;
            }
        }

        public Testserv1()
        {
            //InitializeComponent();

        }

        public List<int> CallA()
        {
            CallACount++;
            return new List<int>() {
                1,2,3,4,5,6
            };
        }

        public List<int> CallB()
        {
            CallBCount++;
            return new List<int>() {
                7,8,9,10,11,12
            };
        }


    }
}

上一代码的输出是:

Calls to A: 1
Calls to B: 6

答案 1 :(得分:0)

鉴于您的问题中提供的信息,没有正当理由认为编译器会转换为字典。事实上,没有理由使用字典。为什么人们希望编译器从方法调用结果中获取唯一键?

提出的问题并未提供足够的信息,甚至无法回复相关的回复。

必要的细节包括: getAs和getBs方法的返回类型是什么? 比较的基础是什么? 循环期望的用法和结果类型是什么?

每个循环都使用不同的对象,假设在完全缺乏直接信息的情况下编译会以任何方式进行优化,基于什么原因?

给出一个更具体的例子,如:

List<string> intersection = new List<string>();

foreach (string a in GetaList())
{
    foreach (string b in GetbList())
    {
        if (a.Equals(b))
        {
            intersection.Add(a);
        }
    }
}

程序集(F11进入步骤,CTRL-F11进行查看程序集)将指示实际为每次迭代调用GetbList,至少在调试模式下。

我相信你的问题的答案是:不,编译器没有为你优化内部方法调用。如果您希望内部方法调用仅发生一次,请在外部循环外进行调用。