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