C# 在LINQ之外,产量有用吗?

C# 在LINQ之外,产量有用吗?,c#,.net,linq,yield,C#,.net,Linq,Yield,当我认为我可以使用yield关键字时,我会后退一步,看看它将如何影响我的项目。我总是返回一个集合而不是一个yilding,因为我觉得维护yilding方法状态的开销并不能给我带来多少好处。在几乎所有返回集合的情况下,我感觉90%的时间,调用方法将迭代集合中的所有元素,或者在整个集合中查找一系列元素 我确实理解它在linq中的有用性,但我觉得只有linq团队在编写这样复杂的可查询对象,从而产生有用的结果 有没有人写过类似或不类似于linq的东西,其中yield很有用?请注意,使用yield时,您将

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

我确实理解它在linq中的有用性,但我觉得只有linq团队在编写这样复杂的可查询对象,从而产生有用的结果


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

请注意,使用yield时,您将在集合上迭代一次,但在构建列表时,您将在集合上迭代两次

以过滤器迭代器为例:

IEnumerator<T>  Filter(this IEnumerator<T> coll, Func<T, bool> func)
{
     foreach(T t in coll)
        if (func(t))  yield return t;
}
IEnumerator筛选器(此IEnumerator coll,Func-Func)
{
foreach(T在coll中)
if(func(t))收益率t;
}
现在,您可以将此链接:

 MyColl.Filter(x=> x.id > 100).Filter(x => x.val < 200).Filter (etc)
MyColl.Filter(x=>x.id>100)。Filter(x=>x.val<200)。Filter(etc)
您的方法将创建(并抛出)三个列表。我的方法只对它迭代一次


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

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


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

收益率很有用,因为它可以节省空间。编程中的大多数优化都在空间(磁盘、内存、网络)和处理之间进行权衡。作为一种编程构造,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
    }
}
static IEnumerable GetAllPeople()
{
返回新列表()
{
新人(){Name=“George”,姓氏=“Bush”,City=“Washington”},
新人(){Name=“亚伯拉罕”,姓氏=“林肯”,City=“华盛顿”},
newperson(){Name=“Joe”,姓氏=“Average”,City=“newyork”}
};
}
静态IEnumerable GetPeopleFrom(此IEnumerable人物,字符串where)
{
foreach(人与人之间的变量)
{
如果(person.City==where)产生返回人;
}
屈服断裂;
}
静态IEnumerable GetPeopleWithInitial(此IEnumerable people,字符串首字母)
{
foreach(人与人之间的变量)
{
如果(人员姓名和首字母)返回人员;
}
屈服断裂;
}
静态void Main(字符串[]参数)
{
var people=GetAllPeople();
foreach(var p in people.GetPeopleFrom(“华盛顿”))
{
//对华盛顿人做点什么
}
foreach(person.GetPeopleWithInitial(“G”)中的var p)
{
//对首字母为G的人做点什么
}
foreach(在people.GetPeopleWithInitial(“p”).GetPeopleFrom(“纽约”)中的var p)
{
//等
}
}
(显然,您不需要在扩展方法中使用yield,它只是创建了一个考虑数据的强大范例。)

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

yield的第一个副作用是它延迟了过滤逻辑的执行,直到您实际需要它为止。因此,如果您创建了IEnumerable类型的变量(具有收益率),但从未对其进行迭代,那么您永远不会执行逻辑或消耗空间,这是一种强大而自由的优化


另一个副作用是,yield在最低的公共集合接口(IEnumerable)上运行,该接口允许创建具有广泛适用性的类库代码。

每当函数返回IEnumerable时,都应该使用“yield”。仅在.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);
      }
        ...
公共静态类函数
{
公共委托T Func();
公共委托T Func(A0 arg0);
公共委托T Func(A0 arg0,A1 arg1);
... 
公共静态IEnumerable筛选器(IEnumerable e,Func filterFunc)
{
foreach(e中的T el)
if(过滤器风(el))
收益率;
}
公共静态IEnumerable映射(IEnumerable e,Func mapFunc)
{
foreach(e中的T el)
收益率收益率mapFunc(el);
}
...

在以前的一家公司,我发现自己写的循环如下:

for (DateTime date = schedule.StartDate; date <= schedule.EndDate; 
     date = date.AddDays(1))
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;
    }
}

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

我不确定C#对yield()的实现,但在动态语言中,它比创建整个集合要高效得多。在许多情况下,它使处理比RAM大得多的数据集变得更容易。

我在非linq代码中使用了yeild,比如(假设函数不在同一类中):

public IEnumerable GetData()
{
foreach(_someInternalDataCollection中的字符串名称)
{
产生返回名称;
}
}
...
公共无效剂量测定法()
{
foreach(GetData()中的字符串值)
{
//…使用不会修改\u someInternalDataCollection的值执行某些操作
}
}

您必须小心不要无意中修改GetData()函数正在迭代的集合,否则它将抛出异常。

yield
是为C#2(在C#3中的Linq之前)开发的

我们在大型企业C#2Web应用程序中大量使用了它
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
    }
}
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;
    }
}
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;
}
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();
            }
        }
    }    
}