在 LINQ 之外,yield 有用吗?

发布于 2024-07-10 01:09:25 字数 285 浏览 8 评论 0原文

当我认为我可以使用yield关键字时,我会退后一步,看看它将如何影响我的项目。 我总是最终返回一个集合而不是 yielding,因为我觉得维护 yielding 方法状态的开销并没有给我带来太多好处。 在几乎所有返回集合的情况下,我觉得 90% 的情况下,调用方法将迭代集合中的所有元素,或者将在整个集合中寻找一系列元素。

我确实理解它在 linq 中的用处,但我觉得只有 linq 团队正在编写如此复杂的可查询对象,yield 才有用。

有没有人写过类似或不类似 linq 的东西,其中yield 很有用?

When ever I think I can use the yield keyword, I take a step back and look at how it will impact my project. I always end up returning a collection instead of yeilding because I feel the overhead of maintaining the state of the yeilding method doesn't buy me much. In almost all cases where I am returning a collection I feel that 90% of the time, the calling method will be iterating over all elements in the collection, or will be seeking a series of elements throughout the entire collection.

I do understand its usefulness in linq, but I feel that only the linq team is writing such complex queriable objects that yield is useful.

Has anyone written anything like or not like linq where yield was useful?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

太阳公公是暖光 2024-07-17 01:09:25

请注意,使用yield时,您将迭代集合一次,但是当您构建列表时,您将迭代它两次。

以过滤器迭代器为例:

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)

您的方法将创建(并抛出)三个列表。 我的方法只迭代一次。

此外,当您返回集合时,您会强制用户执行特定的实现。 迭代器更通用。

Note that with yield, you are iterating over the collection once, but when you build a list, you'll be iterating over it twice.

Take, for example, a filter iterator:

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

Now, you can chain this:

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

You method would be creating (and tossing) three lists. My method iterates over it just once.

Also, when you return a collection, you are forcing a particular implementation on you users. An iterator is more generic.

树深时见影 2024-07-17 01:09:25

我确实理解它在 linq 中的用处,但我觉得只有 linq 团队正在编写如此复杂的可查询对象,yield 才有用。

Yield 在 .NET 2.0 中实现后就很有用,而这早在人们想到 LINQ 之前。

我为什么要写这个函数:

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

当我可以使用yield,并节省无缘无故创建临时列表的精力和复杂性时:

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

它还可以具有巨大的性能优势。 如果您的代码仅碰巧使用集合的前 5 个元素,那么使用 Yield 通常可以避免加载超过该点的任何内容。 如果您构建一个集合然后将其返回,您会浪费大量时间和空间来加载您永远不需要的东西。

我可以继续下去....

I do understand its usefulness in linq, but I feel that only the linq team is writing such complex queriable objects that yield is useful.

Yield was useful as soon as it got implemented in .NET 2.0, which was long before anyone ever thought of LINQ.

Why would I write this function:

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

When I can use yield, and save the effort and complexity of creating a temporary list for no good reason:

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

It can also have huge performance advantages. If your code only happens to use the first 5 elements of the collection, then using yield will often avoid the effort of loading anything past that point. If you build a collection then return it, you waste a ton of time and space loading things you'll never need.

I could go on and on....

做个少女永远怀春 2024-07-17 01:09:25

我最近必须以 Expression 类的形式表示数学表达式。 在评估表达式时,我必须使用后序树遍历来遍历树结构。 为了实现这一点,我实现了 IEnumerable像这样:

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 来遍历表达式。 您还可以添加属性来根据需要更改遍历算法。

I recently had to make a representation of mathematical expressions in the form of an Expression class. When evaluating the expression I have to traverse the tree structure with a post-order treewalk. To achieve this I implemented IEnumerable<T> like this:

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

Then I can simply use a foreach to traverse the expression. You can also add a Property to change the traversal algorithm as needed.

苍暮颜 2024-07-17 01:09:25

在以前的一家公司,我发现自己编写了这样的循环:

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

通过一个非常简单的迭代器块,我能够将其更改为:

foreach (DateTime date in schedule.DateRange)

在我看来,它使代码更易于阅读。

At a previous company, I found myself writing loops like this:

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

With a very simple iterator block, I was able to change this to:

foreach (DateTime date in schedule.DateRange)

It made the code a lot easier to read, IMO.

执笏见 2024-07-17 01:09:25

yield 是为 C#2 开发的(早于 C#3 中的 Linq)。

在处理数据访问和大量重复计算时,我们在大型企业 C#2 Web 应用程序中大量使用它。

当您有一些要多次点击的元素时,集合就很棒。

然而,在许多数据访问场景中,您拥有大量元素,但不一定需要在一个巨大的集合中传递。

这本质上就是 SqlDataReader 的作用 - 它是一个仅向前的自定义枚举器。

yield 让您可以用最少的代码快速编写您自己的自定义枚举器。

yield 所做的一切都可以在 C#1 中完成 - 只需要大量代码即可完成。

Linq 确实最大化了收益行为的价值,但它肯定不是唯一的应用程序。

yield was developed for C#2 (before Linq in C#3).

We used it heavily in a large enterprise C#2 web application when dealing with data access and heavily repeated calculations.

Collections are great any time you have a few elements that you're going to hit multiple times.

However in lots of data access scenarios you have large numbers of elements that you don't necessarily need to pass round in a great big collection.

This is essentially what the SqlDataReader does - it's a forward only custom enumerator.

What yield lets you do is quickly and with minimal code write your own custom enumerators.

Everything yield does could be done in C#1 - it just took reams of code to do it.

Linq really maximises the value of the yield behaviour, but it certainly isn't the only application.

韵柒 2024-07-17 01:09:25

每当你的函数返回 IEnumerable 时,你应该使用“yielding”。 不在.Net中> 仅 3.0。

.Net 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);
      }
        ...

Whenever your function returns IEnumerable you should use "yielding". Not in .Net > 3.0 only.

.Net 2.0 example:

  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);
      }
        ...
淡淡離愁欲言轉身 2024-07-17 01:09:25

我是 C# 中 Yield 的忠实粉丝。 在大型自制框架中尤其如此,其中方法或属性通常返回作为另一个 IEnumerable 的子集的 List。 我看到的好处是:

  • 使用yield的方法的返回值是不可变的,
  • 只有在它是延迟或延迟执行变量时才迭代列表
  • ,这意味着返回值的代码直到需要时才会执行(尽管这可以如果你不知道你在做什么,那么你就咬你了)
  • 源列表发生了变化,你不必调用来获取另一个 IEnumerable,你只需再次迭代 IEnumerable
  • yield

的另一个巨大好处是当你的方法可能会返回数百万个值时。 如此之多,以至于在方法返回列表之前构建列表就有可能耗尽内存。 使用yield,该方法可以创建并返回数百万个值,只要调用者也不存储每个值。 因此它有利于大规模数据处理/聚合操作

I am a huge Yield fan in C#. This is especially true in large homegrown frameworks where often methods or properties return List that is a sub-set of another IEnumerable. The benefits that I see are:

  • the return value of a method that uses yield is immutable
  • you are only iterating over the list once
  • it a late or lazy execution variable, meaning the code to return the values are not executed until needed (though this can bite you if you dont know what your doing)
  • of the source list changes, you dont have to call to get another IEnumerable, you just iterate over IEnumeable again
  • many more

One other HUGE benefit of yield is when your method potentially will return millions of values. So many that there is the potential of running out of memory just building the List before the method can even return it. With yield, the method can just create and return millions of values, and as long the caller also doesnt store every value. So its good for large scale data processing / aggregating operations

徒留西风 2024-07-17 01:09:25

就我个人而言,我还没有发现我在日常编程中使用了yield。 然而,我最近开始使用 Robotics Studio 示例,发现yield 在那里被广泛使用,所以我还看到它与 CCR(并发和协调运行时)结合使用,其中存在异步和并发问题。

不管怎样,我仍在努力解决这个问题。

Personnally, I haven't found I'm using yield in my normal day-to-day programming. However, I've recently started playing with the Robotics Studio samples and found that yield is used extensively there, so I also see it being used in conjunction with the CCR (Concurrency and Coordination Runtime) where you have async and concurrency issues.

Anyway, still trying to get my head around it as well.

请你别敷衍 2024-07-17 01:09:25

产量很有用,因为它可以节省空间。 大多数编程优化都会在空间(磁盘、内存、网络)和处理之间进行权衡。 Yield 作为一种编程构造,允许您按顺序多次迭代集合,而无需为每次迭代提供单独的集合副本。

考虑这个例子:(

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

显然你不需要将yield与扩展方法一起使用,它只是创建了一个强大的范例来思考数据。)

正如你所看到的,如果你有很多这样的“过滤”方法(但它可以是任何对人员列表进行某些工作的方法)您可以将其中许多人链接在一起,而无需为每个步骤提供额外的存储空间。 这是提升编程语言 (C#) 以更好地表达解决方案的一种方法。

Yield 的第一个副作用是它会延迟过滤逻辑的执行,直到您真正需要它为止。 因此,如果您创建 IEnumerable<> 类型的变量; (带有收益)但永远不会迭代它,你永远不会执行逻辑或消耗空间,这是一个强大且免费的优化。

另一个副作用是,yield 在最低通用集合接口 (IEnumerable<>) 上运行,这使得能够创建具有广泛适用性的类库代码。

Yield is useful because it saves you space. Most optimizations in programming makes a trade off between space (disk, memory, networking) and processing. Yield as a programming construct allows you to iterate over a collection many times in sequence without needing a separate copy of the collection for each iteration.

consider this example:

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

(Obviously you are not required to use yield with extension methods, it just creates a powerful paradigm to think about data.)

As you can see, if you have a lot of these "filter" methods (but it can be any kind of method that does some work on a list of people) you can chain many of them together without requiring extra storage space for each step. This is one way of raising the programming language (C#) up to express your solutions better.

The first side-effect of yield is that it delays execution of the filtering logic until you actually require it. If you therefore create a variable of type IEnumerable<> (with yields) but never iterate through it, you never execute the logic or consume the space which is a powerful and free optimization.

The other side-effect is that yield operates on the lowest common collection interface (IEnumerable<>) which enables the creation of library-like code with wide applicability.

终难愈 2024-07-17 01:09:25

我不确定 C# 的 Yield() 实现,但在动态语言上,它比创建整个集合要高效得多。 在许多情况下,它可以轻松处理比 RAM 大得多的数据集。

I'm not sure about C#'s implementation of yield(), but on dynamic languages, it's far more efficient than creating the whole collection. on many cases, it makes it easy to work with datasets much bigger than RAM.

风为裳 2024-07-17 01:09:25

请注意,yield 允许您以“懒惰”的方式做事。 我所说的惰性是指,直到实际请求该元素时,才会对 IEnumberable 中的下一个元素进行评估。 这使您有能力做几件不同的事情。 一是你可以产生一个无限长的列表,而不需要实际进行无限的计算。 其次,您可以返回函数应用程序的枚举。 仅当您迭代列表时才会应用这些函数。

Note that yield allows you to do things in a "lazy" way. By lazy, I mean that the evaluation of the next element in the IEnumberable is not done until the element is actually requested. This allows you the power to do a couple of different things. One is that you could yield an infinitely long list without the need to actually make infinite calculations. Second, you could return an enumeration of function applications. The functions would only be applied when you iterate through the list.

您的好友蓝忘机已上羡 2024-07-17 01:09:25

我在像这样的非 linq 代码中使用了 yeild (假设函数不在同一个类中):

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() 函数正在迭代的集合,否则它将抛出一个例外。

I've used yeild in non-linq code things like this (assuming functions do not live in same class):

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

You have to be careful not to inadvertently modify the collection that your GetData() function is iterating over though, or it will throw an exception.

幻想少年梦 2024-07-17 01:09:25

一般来说,产量非常有用。 它采用 ruby​​ 以及其他支持函数式编程的语言,因此它就像与 linq 绑定在一起。 恰恰相反,linq 在风格上是函数式的,所以它使用了yield。

我遇到了一个问题,我的程序在某些后台任务中使用了大量的 cpu。 我真正想要的是仍然能够像平常一样编写函数,以便我可以轻松地阅读它们(即整个线程与基于事件的参数)。 如果这些功能占用了太多的CPU,仍然能够分解这些功能。 产量对于这个来说是完美的。 我写了一篇博客文章 关于这一点,所有人都可以获取源代码:)

Yield is very useful in general. It's in ruby among other languages that support functional style programming, so its like it's tied to linq. It's more the other way around, that linq is functional in style, so it uses yield.

I had a problem where my program was using a lot of cpu in some background tasks. What I really wanted was to still be able to write functions like normal, so that I could easily read them (i.e. the whole threading vs. event based argument). And still be able to break the functions up if they took too much cpu. Yield is perfect for this. I wrote a blog post about this and the source is available for all to grok :)

贵在坚持 2024-07-17 01:09:25

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

yield 的另一个有趣的优点是调用者无法将返回值转换为原始集合类型并修改您的内部集合

The System.Linq IEnumerable extensions are great, but sometime you want more. For example, consider the following extension:

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

Another interesting advantage of yielding is that the caller cannot cast the return value to the original collection type and modify your internal collection

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文