在ElementAt扩展方法中使用Goto

本文关键字:Goto 方法 ElementAt 扩展 | 更新日期: 2023-09-27 18:13:54

我正在查看Linq扩展方法ElementAt的。net Reflector生成的一些代码,我看到了以下代码:

public static TSource ElementAt<TSource>(this IEnumerable<TSource> source, int index)
{
    TSource current;
    using (IEnumerator<TSource> enumerator = source.GetEnumerator())
    {
    Label_0036:
        if (!enumerator.MoveNext())
        {
            throw Error.ArgumentOutOfRange("index");
        }
        if (index == 0)
        {
            current = enumerator.Current;
        }
        else
        {
            index--;
            goto Label_0036;
        }
    }
    return current;
}

我认为你不用goto语句也能写出同样的东西。例如:

public static TSource ElementAtBis<TSource>(this IEnumerable<TSource> source, int index)
{
    using (IEnumerator<TSource> enumerator = source.GetEnumerator())
    {
        while (enumerator.MoveNext())
        {
            if (index == 0)
            {
                return enumerator.Current;
            }
            else
            {
                index--;
            }
        }
        throw new ArgumentOutOfRangeException("index");
    }
}

所以我想知道为什么ElementAt是这样写的。这里有什么惯例吗?也许规则是只有一个返回语句,也就是函数的最后一行?或者我可能忽略了一些关于性能的东西?或者是。net反射器有问题?

作为旁注,方法ElementAtOrDefault不使用goto语句:
public static TSource ElementAtOrDefault<TSource>(this IEnumerable<TSource> source, int index)
{
    using (IEnumerator<TSource> enumerator = source.GetEnumerator())
    {
        while (enumerator.MoveNext())
        {
            if (index == 0)
            {
                return enumerator.Current;
            }
            index--;
        }
    }
    return default(TSource);
}

在ElementAt扩展方法中使用Goto

这都是反编译器的问题。下一段代码是通过DotPeek:

反编译mscorlib程序集的方法ElementAt生成的:
public static TSource ElementAt<TSource>(this IEnumerable<TSource> source, int index)
//...omitted code for validation
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
   while (enumerator.MoveNext())
   {
      if (index == 0)
         return enumerator.Current;
      --index;
   }
   throw Error.ArgumentOutOfRange("index");
}

IL指令没有while结构。下面的代码演示了这一点:

while(true)
{
    Console.WriteLine ("hi there");
}

被编译成:

IL_0000:  ldstr       "hi there"
IL_0005:  call        System.Console.WriteLine
IL_000A:  br.s        IL_0000    //unconditionaly transfers control to IL_0000. It's like goto IL_0000; but in IL

我想是。net Reflector反编译了这样的代码,这里的原始代码没有任何goto

 public static TSource ElementAt</tsource,><tsource>(this IEnumerable</tsource><tsource> source, int index) {
            if (source == null) throw Error.ArgumentNull("source");
            IList</tsource><tsource> list = source as IList</tsource><tsource>;
            if (list != null) return list[index];
            if (index < 0) throw Error.ArgumentOutOfRange("index");
            using (IEnumerator</tsource><tsource> e = source.GetEnumerator()) {
                while (true) {
                    if (!e.MoveNext()) throw Error.ArgumentOutOfRange("index");
                    if (index == 0) return e.Current;
                    index--;
                }
            }
        }