Кто-то может демистифицировать ключевое слово урожая?

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

Также остерегаются этого, возможно, сначала необходимо думать о коде вместо того, чтобы непосредственно бросить психо в него. Таким образом, я соглашаюсь с Devin, тот алгоритм, оптимизация должна быть на первом месте и у них мог бы быть меньший шанс нежелательного sideeffects.

28
задан xan 11 February 2010 в 00:56
поделиться

9 ответов

Безусловно, лучшее объяснение этого (которое я видел) - это книга Джона Скита - и эта глава бесплатно! Глава 6, Подробно о C # . Я не могу добавить сюда ничего, что не покрыто.

Тогда купите книгу; вы станете лучшим программистом на C # для этого.


Q: Почему я не написал здесь более длинный ответ (перефразированный из комментариев); просто. Как отмечает Эрик Липперт ( здесь ), конструкция yield (и магия, которая стоит за ней) является единственным наиболее сложным битом кода в компиляторе C # , и пытаться описать это кратким ответом здесь в лучшем случае наивно. В yield так много нюансов, что, IMO, лучше обратиться к уже существующему (и полностью квалифицированному) ресурсу.

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

32
ответ дан 28 November 2019 в 02:18
поделиться

Можете ли вы просто переключить поведение в зависимости от параметра.class.name? Это уродливо, но, если я правильно понимаю, у вас есть единственный метод, которому вы будете передавать несколько типов - вам придется как-то различать.

Или просто добавьте метод, который обрабатывает параметр типа массива. Его поведение немного отличается, поэтому может иметь смысл дополнительный метод.

или IEnumerator , и он заставляет компилятор генерировать класс, который реализует необходимые каналы для использования итератора. Например,

public IEnumerator<int> SequenceOfOneToThree() {
    yield return 1;
    yield return 2;
    yield return 3;
}

Учитывая вышеизложенное, компилятор сгенерирует класс, который реализует IEnumerator , IEnumerable и IDisposable (фактически он также будет реализовывать неуниверсальные версии IEnumerable и IEnumerator ).

Это позволяет вызывать метод SequenceOfOneToThree в цикле foreach как этот

foreach(var number in SequenceOfOneToThree) {
    Console.WriteLine(number);
}

Итератор - это конечный автомат, поэтому каждый раз, когда вызывается yield , записывается позиция в методе. Если итератор перемещается к следующему элементу, метод возобновляется сразу после этой позиции. Итак, первая итерация возвращает 1 и отмечает эту позицию. Следующий итератор возобновляет работу сразу после одного и возвращает 2 и так далее.

Излишне говорить, что вы можете сгенерировать последовательность любым способом, поэтому вам не нужно жестко кодировать числа, как это сделал я. Кроме того, если вы хотите разорвать цикл, вы можете использовать yield break .

30
ответ дан 28 November 2019 в 02:18
поделиться

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

операторы yield return и yield break чаще всего используются для обеспечения «отложенной оценки» коллекции.

Это означает, что когда вы получаете значение метода, использующего yield return, набор вещей, которые вы пытаетесь сделать get еще не существуют вместе (по сути, он пуст). По мере того, как вы просматриваете их (используя foreach), он выполнит метод в это время и получит следующий элемент в перечислении.

Определенные свойства и методы приведут к одновременной оценке всего перечисления (например, «Count») .

Здесь ' Быстрый пример разницы между возвратом коллекции и возвратом yield:

string[] names = { "Joe", "Jim", "Sam", "Ed", "Sally" };

public IEnumerable<string> GetYieldEnumerable()
{
    foreach (var name in names)
        yield return name;
}

public IEnumerable<string> GetList()
{
    var list = new List<string>();
    foreach (var name in names)
        list.Add(name);

    return list;
}

// we're going to execute the GetYieldEnumerable() method
// but the foreach statement inside it isn't going to execute
var yieldNames = GetNamesEnumerable();

// now we're going to execute the GetList() method and
// the foreach method will execute
var listNames = GetList();

// now we want to look for a specific name in yieldNames.
// only the first two iterations of the foreach loop in the 
// GetYieldEnumeration() method will need to be called to find it.
if (yieldNames.Contains("Jim")
    Console.WriteLine("Found Jim and only had to loop twice!");

// now we'll look for a specific name in listNames.
// the entire names collection was already iterated over
// so we've already paid the initial cost of looping through that collection.
// now we're going to have to add two more loops to find it in the listNames
// collection.
if (listNames.Contains("Jim"))
    Console.WriteLine("Found Jim and had to loop 7 times! (5 for names and 2 for listNames)");

Это также можно использовать, если вам нужно получить ссылку на Enumeration до того, как исходные данные будут иметь значения. Например, если коллекция имен не была полной для начала:

string[] names = { "Joe", "Jim", "Sam", "Ed", "Sally" };

public IEnumerable<string> GetYieldEnumerable()
{
    foreach (var name in names)
        yield return name;
}

public IEnumerable<string> GetList()
{
    var list = new List<string>();
    foreach (var name in names)
        list.Add(name);

    return list;
}

var yieldNames = GetNamesEnumerable();

var listNames = GetList();

// now we'll change the source data by renaming "Jim" to "Jimbo"
names[1] = "Jimbo";

if (yieldNames.Contains("Jimbo")
    Console.WriteLine("Found Jimbo!");

// Because this enumeration was evaluated completely before we changed "Jim"
// to "Jimbo" it isn't going to be found
if (listNames.Contains("Jimbo"))
    // this can't be true
else
   Console.WriteLine("Couldn't find Jimbo, because he wasn't there when I was evaluated.");
18
ответ дан 28 November 2019 в 02:18
поделиться

Взгляните на документацию MSDN и пример. По сути, это простой способ создать итератор в C #.

public class List
{
    //using System.Collections;
    public static IEnumerable Power(int number, int exponent)
    {
        int counter = 0;
        int result = 1;
        while (counter++ < exponent)
        {
            result = result * number;
            yield return result;
        }
    }

    static void Main()
    {
        // Display powers of 2 up to the exponent 8:
        foreach (int i in Power(2, 8))
        {
            Console.Write("{0} ", i);
        }
    }
}
6
ответ дан 28 November 2019 в 02:18
поделиться

Ключевое слово yield - это удобный способ написать IEnumerator . Например:

public static IEnumerator<int> Range(int from, int to)
{
    for (int i = from; i < to; i++)
    {
        yield return i;
    }
}

преобразуется компилятором C # в нечто похожее на:

public static IEnumerator<int> Range(int from, int to)
{
    return new RangeEnumerator(from, to);
}

class RangeEnumerator : IEnumerator<int>
{
    private int from, to, current;

    public RangeEnumerator(int from, int to)
    {
        this.from = from;
        this.to = to;
        this.current = from;
    }

    public bool MoveNext()
    {
        this.current++;
        return this.current < this.to;
    }

    public int Current
    {
        get
        {
            return this.current;
        }
    }
}
10
ответ дан 28 November 2019 в 02:18
поделиться

Серия статей Эрика Уайта по функциональному программированию ее стоит прочитать целиком, но статья в Yield является столь же ясным объяснением, как и я » видела.

4
ответ дан 28 November 2019 в 02:18
поделиться

yield напрямую не связан с LINQ, а скорее с блоками итератора . Связанная статья MSDN дает подробные сведения об этой языковой функции. См. Особенно раздел Использование итераторов . Подробную информацию о блоках итератора см. В недавнем блоге Эрика Липперта , сообщения об этой функции. Общую концепцию см. В статье об итераторах в Википедии .

3
ответ дан 28 November 2019 в 02:18
поделиться

Позвольте мне добавить ко всему этому. Доходность - это не ключевое слово. Он будет работать только в том случае, если вы используете «yield return», кроме этого, он будет работать как обычная переменная.

Он используется для возврата итератора из функции. Вы можете поискать дальше. Я рекомендую поискать "Возвращаемый массив против итератора"

0
ответ дан 28 November 2019 в 02:18
поделиться

Вы можете сделать это самостоятельно через 20 сек. Например, в C #
- Создайте новое приложение WinForms
- Создайте новое SqlConnection (connectionString)
- Исключение => Неверная строка подключения
Недостаток .NET - необходимость глубокого копирования списка вручную.

Я использую это:

static public IEnumerable<SpotPlacement> CloneList(List<SpotPlacement> spotPlacements)
{
    foreach (SpotPlacement sp in spotPlacements)
    {
        yield return (SpotPlacement)sp.Clone();
    }
}

И в другом месте:

public object Clone()
{
    OrderItem newOrderItem = new OrderItem();
    ...
    newOrderItem._exactPlacements.AddRange(SpotPlacement.CloneList(_exactPlacements));
    ...
    return newOrderItem;
}

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

РЕДАКТИРОВАТЬ:

Еще лучше использовать общий клонер списка:

class Utility<T> where T : ICloneable
{
    static public IEnumerable<T> CloneList(List<T> tl)
    {
        foreach (T t in tl)
        {
            yield return (T)t.Clone();
        }
    }
}
2
ответ дан 28 November 2019 в 02:18
поделиться
Другие вопросы по тегам:

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