В C#, почему анонимный метод не может содержать оператор урожая?

Следующее поколение Visual C ++, которое должно выйти целых две недели, включает в себя std::thread библиотеки, а G ++ уже, если я не ошибаюсь. Я бы не посчитал это против этого. И вот в чем дело: C ++ 11 действительно очень важен. Если ваше рабочее место не планирует мигрировать, я найду другое рабочее место.

85
задан Kenan E. K. 1 August 2009 в 23:42
поделиться

4 ответа

Я не вижу необходимости в этом методе. Просто используйте Where ().

 var sublist = list.Where( expression.Compile() ).ToList();

Или, что еще лучше, определите выражение как встроенное лямбда-выражение. почему "урожайность" не допускается внутри анонимный метод или лямбда-выражение

A:

Хороший вопрос. Я бы хотел иметь анонимные блоки итератора. Это было бы совершенно потрясающе иметь возможность строить себе небольшой генератор последовательности на месте, которые закрыли местные переменные. Причина, почему нет просто: преимущества не перевешивают затраты. Великолепие создание генераторов последовательностей на месте - это на самом деле довольно маленький в большом схема вещей и именные приемы делать работу достаточно хорошо в большинстве сценарии. Так что пользы нет

Затраты велики. Итератор переписывание - самое сложное преобразование в компиляторе, и переписывание анонимного метода - это второй по сложности. Анонимный методы могут быть внутри других анонимных методы, а анонимные методы могут быть внутри блоков итератора. Следовательно, то, что мы делаем, сначала мы переписываем все анонимные методы, чтобы они стали методы класса закрытия. Это второе и последнее, что компилятор выполняет перед отправкой IL для метода. Как только этот шаг будет выполнен, итератор переписчик может предположить, что нет анонимные методы в итераторе блок; они все были переписаны уже. Следовательно, итератор переписчик может просто сосредоточиться на переписывая итератор, без беспокоясь, что может быть там нереализованный анонимный метод.

Кроме того, блоки итератора никогда не "вкладываются", в отличие от анонимных методов. Итератор rewriter может предполагать, что все итераторы блоки являются «верхним уровнем».

Если анонимные методы разрешены содержат блоки итератора, тогда оба эти предположения выпадают из окна. У вас может быть блок итератора, который содержит анонимный метод, который содержит анонимный метод, который содержит блок итератора, который содержит анонимный метод и ... фу. Теперь надо написать переписывание проход, который может обрабатывать вложенный итератор блоки и вложенные анонимные методы в в то же время, объединяя два наших самых сложные алгоритмы в один далеко более сложный алгоритм. Было бы быть очень сложным в разработке, реализации, и тест. Мы достаточно умны, чтобы делать так что я уверен. У нас есть умная команда Вот. Но мы не хотим брать на себя это большое бремя для "приятно иметь но не обязательная »функция. - Эрик

105
ответ дан 24 November 2019 в 08:22
поделиться

Эрик Липперт написал отличную серию статей об ограничениях (и проектных решениях, влияющих на этот выбор) для блоков итераторов

В частности, блоки итераторов реализованы некоторым сложным кодом компилятора. трансформации. Эти преобразования будут влиять на преобразования, которые происходят внутри анонимных функций или лямбда-выражений, так что в определенных обстоятельствах они оба будут пытаться «преобразовать» код в какую-то другую конструкцию, несовместимую с другой.

В результате им запрещено взаимодействия.

Как блоки итератора работают под капотом, хорошо здесь .

В качестве простого примера несовместимости:

public IList<T> GreaterThan<T>(T t)
{
    IList<T> list = GetList<T>();
    var items = () => {
        foreach (var item in list)
            if (fun.Invoke(item))
                yield return item; // This is not allowed by C#
    }

    return items.ToList();
}

Компилятор одновременно хочет преобразовать это во что-то вроде:

// inner class
private class Magic
{
    private T t;
    private IList<T> list;
    private Magic(List<T> list, T t) { this.list = list; this.t = t;}

    public IEnumerable<T> DoIt()
    {
        var items = () => {
            foreach (var item in list)
                if (fun.Invoke(item))
                    yield return item;
        }
    }
}

public IList<T> GreaterThan<T>(T t)
{
    var magic = new Magic(GetList<T>(), t)
    var items = magic.DoIt();
    return items.ToList();
}

и в то же время аспект итератора пытается сделать свою работу по созданию небольшого конечного автомата. Некоторые простые примеры могут работать с достаточным количеством проверок работоспособности (сначала имея дело с (возможно, произвольными) вложенными замыканиями), а затем проверять, могут ли результирующие классы самого нижнего уровня быть преобразованы в конечные автоматы итератора.

Однако это будет

  1. ] Довольно много работы.
  2. Невозможно было бы работать во всех случаях без, по крайней мере, аспекта блока итератора, способного предотвратить применение аспекта закрытия определенных преобразований для повышения эффективности (например, продвижение локальных переменных в переменные экземпляра, а не полноценный класс закрытия).
    • Если бы существовала хотя бы небольшая вероятность совпадения там, где это было невозможно или достаточно сложно не реализовать, то количество возникающих проблем поддержки, вероятно, было бы большим, поскольку тонкие критические изменения были бы потеряны для многих пользователей.
  3. Это можно очень легко обойти.

В вашем примере так:

public IList<T> Find<T>(Expression<Func<T, bool>> expression) 
    where T : class, new()
{
    return FindInner(expression).ToList();
}

private IEnumerable<T> FindInner<T>(Expression<Func<T, bool>> expression) 
    where T : class, new()
{
    IList<T> list = GetList<T>();
    var fun = expression.Compile();
    foreach (var item in list)
        if (fun.Invoke(item))
            yield return item;
}
21
ответ дан 24 November 2019 в 08:22
поделиться

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

Однако анонимные методы уже являются частью «магии компилятора» в том смысле, что метод будет извлечен либо в в существующем классе или даже в совершенно новом классе, в зависимости от того, имеет ли он дело с локальными переменными или нет.

Кроме того, методы итератора, использующие yield , также реализованы с использованием магии компилятора.

Я предполагаю, что один из этих двух делает код неидентифицируемым для другого волшебства, и было решено не тратить время на выполнение этой работы для текущих версий компилятора C #.Конечно, это может быть вовсе не осознанный выбор, и что он просто не работает, потому что никто не подумал его реализовать.

Для 100% точного вопроса я предлагаю вам использовать Microsoft Connect ] и задайте вопрос, я уверен, что взамен вы получите что-нибудь полезное.

3
ответ дан 24 November 2019 в 08:22
поделиться

Я бы сделал следующее:

IList<T> list = GetList<T>();
var fun = expression.Compile();

return list.Where(item => fun.Invoke(item)).ToList();

Конечно, вам понадобится System.Core.dll, на который имеется ссылка в .NET 3.5 для метода Linq. И включают:

using System.Linq;

Ура,

Хитрый

1
ответ дан 24 November 2019 в 08:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: