【问题标题】:Foreach loop, is it faster to derive the iterated collection outside the loop statement?Foreach循环,在循环语句之外派生迭代集合是否更快?
【发布时间】:2013-10-16 19:31:39
【问题描述】:

哪种方法更快,或者两者都编译到相同的 IL?

        var myCollection = new Dictionary<string, string>(){{"a", "aa"}, {"b", "bb"}, {"c", "bb"}};
        foreach (var uniqueItem in myCollection.Values.Distinct())
        {
            //do something
        }

        var myCollection = new Dictionary<string, string>() { { "a", "aa" }, { "b", "bb" }, { "c", "bb" } };
        var uniqueItems = myCollection.Values.Distinct();
        foreach (var uniqueItem in uniqueItems)
        {
            //do something
        }

【问题讨论】:

    标签: c# foreach iteration enumerable


    【解决方案1】:

    Release 模式下,两者产生相同的 IL 并且性能将相同(假设以后不使用 uniqueItems 变量)。

    在调试中,您会在启动 foreach 之前看到另一个分配。

    编辑 - 生成的 IL

    private static void V1(Dictionary<string, string> myCollection)
    {
        foreach (var uniqueItem in myCollection.Values.Distinct())
        {
        }
    }
    

    会编译成:

    .method private hidebysig static void V1(class [mscorlib]System.Collections.Generic.Dictionary`2<string, string> myCollection) cil managed
    {
        .maxstack 2
        .locals init (
            [0] string uniqueItem,
            [1] class [mscorlib]System.Collections.Generic.IEnumerator`1<string> CS$5$0000,
            [2] bool CS$4$0001)
        L_0000: nop 
        L_0001: nop 
        L_0002: ldarg.0 
        L_0003: callvirt instance class [mscorlib]System.Collections.Generic.Dictionary`2/ValueCollection<!0, !1> [mscorlib]System.Collections.Generic.Dictionary`2<string, string>::get_Values()
        L_0008: call class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0> [System.Core]System.Linq.Enumerable::Distinct<string>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>)
        L_000d: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> [mscorlib]System.Collections.Generic.IEnumerable`1<string>::GetEnumerator()
        L_0012: stloc.1 
        L_0013: br.s L_001e
        L_0015: ldloc.1 
        L_0016: callvirt instance !0 [mscorlib]System.Collections.Generic.IEnumerator`1<string>::get_Current()
        L_001b: stloc.0 
        L_001c: nop 
        L_001d: nop 
        L_001e: ldloc.1 
        L_001f: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
        L_0024: stloc.2 
        L_0025: ldloc.2 
        L_0026: brtrue.s L_0015
        L_0028: leave.s L_003a
        L_002a: ldloc.1 
        L_002b: ldnull 
        L_002c: ceq 
        L_002e: stloc.2 
        L_002f: ldloc.2 
        L_0030: brtrue.s L_0039
        L_0032: ldloc.1 
        L_0033: callvirt instance void [mscorlib]System.IDisposable::Dispose()
        L_0038: nop 
        L_0039: endfinally 
        L_003a: nop 
        L_003b: ret 
        .try L_0013 to L_002a finally handler L_002a to L_003a
    }
    

    可以看出,对 distinct 的调用在 *L_0008* 中,并且没有代码跳转到那里或上面,这意味着它只被调用了一次。

    【讨论】:

    • 在 IL 中,无论迭代次数如何,是否会在调试模式下进行一次附加分配?
    • 是的,它将myCollection.Values.Distinct()的值存储在堆栈中,无需再次调用该方法。
    • 还有一个问题,“假设以后不使用 uniqueItems 变量”是什么意思。和枚举数有关系吗?
    猜你喜欢
    • 2022-11-17
    • 2011-03-10
    • 2018-11-18
    • 2014-10-23
    • 1970-01-01
    • 2018-12-25
    • 2013-11-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多