Является ли доход полезным вне LINQ?

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

Я понимаю его полезность в linq, но я чувствую, что только команда linq пишет такие сложные запрашиваемые объекты, которые дают полезность.

Кто-нибудь писал что-то вроде или не похоже на linq, где доходность была полезной?

Ответ 1

Недавно мне пришлось сделать представление математических выражений в виде класса Expression. При оценке выражения мне нужно пересечь древовидную структуру с помощью пост-упорядоченной тройки. Для этого я реализовал IEnumerable <T> например:

public IEnumerator<Expression<T>> GetEnumerator()
{
    if (IsLeaf)
    {
        yield return this;
    }
    else
    {
        foreach (Expression<T> expr in LeftExpression)
        {
            yield return expr;
        }
        foreach (Expression<T> expr in RightExpression)
        {
            yield return expr;
        }
        yield return this;
    }
}

Тогда я могу просто использовать foreach для перемещения выражения. Вы также можете добавить свойство, чтобы изменить алгоритм обхода по мере необходимости.

Ответ 2

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

Возьмем, например, итератор фильтра:

IEnumerator<T>  Filter(this IEnumerator<T> coll, Func<T, bool> func)
{
     foreach(T t in coll)
        if (func(t))  yield return t;
}

Теперь вы можете связать это:

 MyColl.Filter(x=> x.id > 100).Filter(x => x.val < 200).Filter (etc)

Ваш метод будет создавать (и бросать) три списка. Мой метод повторяется только один раз.

Кроме того, когда вы возвращаете коллекцию, вы принудительно применяете определенную реализацию к вашим пользователям. Итератор более общий.

Ответ 3

Я понимаю его полезность в linq, но я чувствую, что только команда linq пишет такие сложные запрашиваемые объекты, которые дают полезность.

Выход был полезен, как только он был реализован в .NET 2.0, что было задолго до того, как кто-либо подумал о LINQ.

Зачем мне писать эту функцию:

IList<string> LoadStuff() {
  var ret = new List<string>();
  foreach(var x in SomeExternalResource)
    ret.Add(x);
  return ret;
}

Когда я могу использовать выход и сохранить усилия и сложность создания временного списка без уважительной причины:

IEnumerable<string> LoadStuff() {
  foreach(var x in SomeExternalResource)
    yield return x;
}

Он также может иметь огромные преимущества в производительности. Если ваш код использует только первые 5 элементов коллекции, то использование урожая часто будет избегать усилий по загрузке чего-либо из этого момента. Если вы создадите коллекцию, то верните ее, вы тратите массу времени и места на вещи, которые вам никогда не понадобятся.

Я мог бы продолжать и продолжать....

Ответ 4

В предыдущей компании я обнаружил, что пишу петли следующим образом:

for (DateTime date = schedule.StartDate; date <= schedule.EndDate; 
     date = date.AddDays(1))

С очень простым блоком итератора я смог изменить это на:

foreach (DateTime date in schedule.DateRange)

Это сделало код намного легче читать, ИМО.

Ответ 5

yield был разработан для С# 2 (до Linq в С# 3).

Мы использовали его в большом корпоративном веб-приложении С# 2 при работе с доступом к данным и сильно повторяющимися вычислениями.

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

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

Это, по существу, то, что делает SqlDataReader - это пересылаемый только пользовательский перечислитель.

Что yield позволяет вам быстро и с минимальным кодом писать собственные пользовательские счетчики.

Все yield можно сделать в С# 1 - для этого потребовалось множество кодов кода.

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

Ответ 6

Всякий раз, когда ваша функция возвращает IEnumerable, вы должны использовать "yielding". Только в .Net > 3.0.

. Пример 2.0 2.0:

  public static class FuncUtils
  {
      public delegate T Func<T>();
      public delegate T Func<A0, T>(A0 arg0);
      public delegate T Func<A0, A1, T>(A0 arg0, A1 arg1);
      ... 

      public static IEnumerable<T> Filter<T>(IEnumerable<T> e, Func<T, bool> filterFunc)
      {
          foreach (T el in e)
              if (filterFunc(el)) 
                  yield return el;
      }


      public static IEnumerable<R> Map<T, R>(IEnumerable<T> e, Func<T, R> mapFunc)
      {
          foreach (T el in e) 
              yield return mapFunc(el);
      }
        ...

Ответ 7

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

Ответ 8

Я большой поклонник Yield в С#. Это особенно актуально в крупных домостроенных рамках, где часто методы или свойства возвращают List, который является подмножеством другого IEnumerable. Преимущества, которые я вижу, следующие:

  • возвращаемое значение метода, использующего выход, является неизменным
  • вы повторяете только один раз по списку
  • это поздняя или ленивая переменная выполнения, то есть код для возврата значений не выполняется до тех пор, пока не понадобится (хотя это может укусить вас, если вы не знаете, что делаете)
  • изменений в исходном списке вам не нужно звонить, чтобы получить еще один IEnumerable, вы просто перейдете через IEnumable снова
  • еще много

Еще одно ОГРОМНОЕ преимущество доходности - это когда ваш метод потенциально вернет миллионы значений. Так много, что есть потенциал исчерпания памяти, просто строя список, прежде чем метод может даже вернуть его. С выходом метод может просто создавать и возвращать миллионы значений, и пока вызывающий объект также не сохраняет каждое значение. Таким образом, это хорошо для крупномасштабных операций обработки/агрегации данных.

Ответ 9

Лично я не нашел, что использую урожай в своем обычном ежедневном программировании. Тем не менее, я недавно начал играть с образцами Robotics Studio и обнаружил, что выход широко используется там, поэтому я также вижу, что он используется в сочетании с CCR (Concurrency и временем согласования), где у вас есть асинхронные и concurrency вопросы.

Во всяком случае, я все еще пытаюсь обнять голову.

Ответ 10

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

рассмотрим этот пример:

static IEnumerable<Person> GetAllPeople()
{
    return new List<Person>()
    {
        new Person() { Name = "George", Surname = "Bush", City = "Washington" },
        new Person() { Name = "Abraham", Surname = "Lincoln", City = "Washington" },
        new Person() { Name = "Joe", Surname = "Average", City = "New York" }
    };
}

static IEnumerable<Person> GetPeopleFrom(this IEnumerable<Person> people,  string where)
{
    foreach (var person in people)
    {
        if (person.City == where) yield return person;
    }
    yield break;
}

static IEnumerable<Person> GetPeopleWithInitial(this IEnumerable<Person> people, string initial)
{
    foreach (var person in people)
    {
        if (person.Name.StartsWith(initial)) yield return person;
    }
    yield break;
}

static void Main(string[] args)
{
    var people = GetAllPeople();
    foreach (var p in people.GetPeopleFrom("Washington"))
    {
        // do something with washingtonites
    }

    foreach (var p in people.GetPeopleWithInitial("G"))
    {
        // do something with people with initial G
    }

    foreach (var p in people.GetPeopleWithInitial("P").GetPeopleFrom("New York"))
    {
        // etc
    }
}

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

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

Первым побочным эффектом выхода является то, что он задерживает выполнение логики фильтрации до тех пор, пока вы ее не потребуете. Если вы поэтому создаете переменную типа IEnumerable < > (с выходами), но никогда не повторяете ее, вы никогда не выполняете логику или не потребляете пространство, которое является мощной и бесплатной оптимизацией.

Другим побочным эффектом является то, что выход работает на самом низком общем интерфейсе коллекции (IEnumerable < > ), который позволяет создавать библиотечно-подобный код с широкой применимостью.

Ответ 11

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

Ответ 12

Я использовал yeild в не-linq-коде, что-то вроде этого (предполагая, что функции не живут в одном классе):

public IEnumerable<string> GetData()
{
    foreach(String name in _someInternalDataCollection)
    {
        yield return name;
    }
}

...

public void DoSomething()
{
    foreach(String value in GetData())
    {
        //... Do something with value that doesn't modify _someInternalDataCollection
    }
}

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

Ответ 13

Доходность очень полезна вообще. Он в рубине среди других языков, поддерживающих программирование функционального стиля, поэтому его привязывает к linq. Это наоборот, что linq является функциональным по стилю, поэтому он использует выход.

У меня была проблема, когда моя программа использовала много процессоров в некоторых фоновых задачах. Я действительно хотел, чтобы все еще были в состоянии писать такие функции, как обычные, чтобы я мог их легко прочитать (т.е. Весь аргумент, связанный с потоками и событиями). И все же сможете сломать функции, если они возьмут слишком много CPU. Для этого идеально подходит. Я написал сообщение в блоге об этом, и источник доступен для всех:)

Ответ 14

Расширения System.Linq IEnumerable великолепны, но когда-нибудь вам нужно больше. Например, рассмотрим следующее расширение:

public static class CollectionSampling
{
    public static IEnumerable<T> Sample<T>(this IEnumerable<T> coll, int max)
    {
        var rand = new Random();
        using (var enumerator = coll.GetEnumerator());
        {
            while (enumerator.MoveNext())
            {
                yield return enumerator.Current; 
                int currentSample = rand.Next(max);
                for (int i = 1; i <= currentSample; i++)
                    enumerator.MoveNext();
            }
        }
    }    
}

Еще одно интересное преимущество уступки заключается в том, что вызывающий не может передать возвращаемое значение в исходный тип коллекции и изменить свою внутреннюю коллекцию.