Почему расширения LINQ написаны очень сложно для чтения?

2

Я проверял некоторые из кода, которые составляют расширения LINQ в Reflector, и это тот код, с которым я сталкиваюсь:

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;
}

Есть ли причина, по которой Microsoft должна писать так?

И что означает синтаксис < > в строках типа:

switch (this.<>1__state)

Я никогда не видел, чтобы это было написано перед переменной, только после.

  • 3
    К вашему сведению, я буду делать серию в своем блоге о характеристиках блоков итераторов в июле.
  • 0
    Спасибо, Эрик. Я буду с нетерпением ждать этого наверняка.
Теги:
linq

7 ответов

8

MSIL по-прежнему является действительным кодом 2.x, а имена < > , которые вы видите, автоматически генерируются компиляторами С# 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.

  • 0
    Я думал, что эти типы имен были выбраны, чтобы удостовериться, что выбранные имена не конфликтуют ни с чем, что вы выбираете.
7

Вы видите внутренние кишки <конечных машин которые компилятор С# испускает от вашего имени, когда он обрабатывает итераторы.

У Jon Skeet есть отличные статьи (Детали реализации блока итератора и Итераторы, итераторные блоки и конвейеры данных) по этому вопросу. См. Также главу 6 его .

Ранее было сообщение SO по этому вопросу.

И, наконец, Microsoft Research имеет хорошую статью по этому вопросу.

Прочитайте, пока ваше сердце не будет удовлетворено.

2

Идентификаторы, начинающиеся с < > , не являются допустимыми идентификаторами С#, поэтому я подозреваю, что они используют их для манипулирования именами без страха конфликта, поскольку ни один идентификатор кода С# не может быть таким же.

Что касается того, почему это трудно читать, я подозреваю, что это больше, чем просто его сгенерировать.

  • 0
    Спасибо, но я не понимаю, как другой код будет конфликтовать с этим?
  • 1
    Джоан, другой код не будет конфликтовать с этим. В этом-то и дело. Компилятор выбирает намеренно недопустимые имена идентификаторов для своего сгенерированного кода, чтобы он не мог помешать написанному пользователем коду, который он компилирует.
Показать ещё 1 комментарий
2

Это код, который автоматически генерируется при использовании iterators. < > используется для обеспечения отсутствия коллизий, а также для предотвращения доступа к классам генератора-компилятора непосредственно в вашем коде.

Для получения дополнительной информации см. следующее:

  • 0
    Я не знал, что символы <> действительны для переменных.
  • 5
    Они не действительны в C #, но они в IL. Это означает, что скомпилированный код является абсолютно допустимым, но вы не можете получить доступ к автоматически сгенерированным типам в своем собственном коде. Это также означает, что компилятор может гарантировать, что (недопустимые в C #) имена автоматически сгенерированных типов не будут конфликтовать с (недопустимыми в C #) именами любых других типов.
2

Это типы, которые были автоматически сгенерированы компилятором из методы итератора.

Компилятор будет делать то же самое с вашими итераторами. Например, напишите что-нибудь подобное, а затем взгляните на фактический сгенерированный код в Reflector:

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

    while (true)
    {
        yield return rng.Next();
    }
}
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 и вызывают каждую часть итератора для другого значения поля.

Часть <> является частью сгенерированного имени поля и выбрана так, чтобы не конфликты ни с чем.

0

Вы должны понять, что делает Reflector. Он не возвращает исходный код. Это не то, что написал разработчик в MS.:) Он принимает промежуточный язык (IL) и систематически преобразует его обратно в С# (или VB.NET). При этом он должен придумать подход. Как вы знаете, существует много способов скинуть кошку в код, который в конечном итоге приведет к тому же ИЛ. Отражатель должен выбрать способ переместить обратно от ИЛ на язык более высокого уровня и использовать этот способ каждый раз.

(Исправлено за комментарий, спасибо.)

  • 1
    Это не имена, выбранные отражателем. Это имена, сгенерированные компилятором и используемые в IL, как уже упоминали другие.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню