为什么LINQ扩展以非常难以阅读的方式编写?

时间:2009-06-29 23:08:16

标签: c# .net linq

我正在检查构成Reflector中LINQ扩展的一些代码,这就是我遇到的那种代码:

private bool MoveNext()
{
    bool flag;
    try
    {
        switch (this.<>1__state)
        {
            case 0:
                this.<>1__state = -1;
                this.<set>5__7b = new Set<TSource>(this.comparer);
                this.<>7__wrap7d = this.source.GetEnumerator();
                this.<>1__state = 1;
                goto Label_0092;

            case 2:
                this.<>1__state = 1;
                goto Label_0092;

            default:
                goto Label_00A5;
        }
    Label_0050:
        this.<element>5__7c = this.<>7__wrap7d.Current;
        if (this.<set>5__7b.Add(this.<element>5__7c))
        {
            this.<>2__current = this.<element>5__7c;
            this.<>1__state = 2;
            return true;
        }
    Label_0092:
        if (this.<>7__wrap7d.MoveNext())
        {
            goto Label_0050;
        }
        this.<>m__Finally7e();
    Label_00A5:
        flag = false;
    }
    fault
    {
        this.System.IDisposable.Dispose();
    }
    return flag;
}

微软是否有理由以这种方式撰写?

&lt;&gt;也是什么语法意思,如下所示:

switch (this.<>1__state)

我从未见过它在变量之前写过,只有在。

之后

7 个答案:

答案 0 :(得分:8)

MSIL仍然是有效的2.x代码和&lt;&gt;您看到的名称由C#3.x编译器自动生成。

例如:

public void AttachEvents()
{
    _ctl.Click += (sender,e) => MessageBox.Show( "Hello!" );
}

转换为:

public void AttachEvents()
{
    _ctl.Click += new EventHandler( <>b_1 );
}

private void <>b_1( object sender, EventArgs e )
{
    MessageBox.Show( "Hello!" );
}

我还应该注意,你在Reflector中看到它的原因是你没有打开.NET 3.5优化。转到查看|选项并将优化更改为.NET 3.5,它可以更好地将生成的标识符转换回其lamda表达式。

答案 1 :(得分:7)

当您处理finite state machines时,您会看到C#编译器代表您发出的iterators内部内容。

Jon Skeet在这个主题上有一些很棒的文章(Iterator block implementation detailsIterators, iterator blocks and data pipelines)。另见book的第6章。

此主题有previously个SO帖子。

最后,微软研究院在这个问题上有一个很好的paper

阅读,直到你满意为止。

答案 2 :(得分:2)

以&lt;&gt;开头的标识符不是有效的C#标识符,所以我怀疑他们使用它们来破坏名称而不用担心冲突,因为C#代码中没有标识符可能是相同的。

至于为何难以阅读,我怀疑这更容易产生。

答案 3 :(得分:2)

这是使用iterators时自动生成的代码。 &lt;&gt;用于确保没有冲突,并且还可以防止您直接在代码中访问编译器生成器类。

有关详细信息,请参阅以下内容:

答案 4 :(得分:2)

这些是编译器从iterator methods自动生成的类型。

编译器将对您自己的迭代器执行完全相同的操作。例如,写下这样的东西,然后看一下Reflector中实际生成的代码:

public IEnumerable<int> GetRandom()
{
    Random rng = new Random();

    while (true)
    {
        yield return rng.Next();
    }
}

答案 5 :(得分:2)

这是从迭代器自动生成的状态机,如下所示:

static IEnumerable<Func<KeyValuePair<int, int>>> FunnyMethod() {
    for (var i = 0; i < 10; i++) {
        var localVar = i;
        yield return () => new KeyValuePair(localVar, i);
    }
}

此方法将为所有值返回10.

编译器将这些方法转换为状态机,将状态存储在<>1__state字段中,并调用迭代器的每个部分以获得不同的字段值。

<>部分是生成的字段名称的一部分,选择它是为了不与任何内容冲突。

答案 6 :(得分:0)

你必须了解Reflector的作用。它没有得到源代码。这不是MS的开发人员所写的。 :)它需要中间语言(IL)并系统地将其转换回C#(或VB.NET)。在这样做时,它必须提出一种方法。如你所知,有许多方法可以在代码中对猫进行换肤,最终会产生相同的IL。反射器必须选择一种方法将病房从IL移回到更高级别的语言,并且每次都使用这种方式。

(修正了每条评论,谢谢。)