在我不断追求吸吮的过程中,我试图理解“收益”陈述,但我一直遇到同样的错误。
[someMethod]的主体不能是迭代器块,因为 “System.Collections.Generic.List< ACLASS>”不是迭代器接口类型。
这是我遇到的代码:
foreach (XElement header in headersXml.Root.Elements()){
yield return (ParseHeader(header));
}
我做错了什么?我不能在迭代器中使用yield吗?那有什么意义呢?
在这个例子中,它说List<ProductMixHeader>
不是迭代器接口类型。
ProductMixHeader
是一个自定义类,但我想List
是一个迭代器接口类型,不是吗?
- 编辑 -
感谢所有快速解答。
我知道这个问题不是那么新,而且同样的资源不断涌现
结果我认为我可以返回List<AClass>
作为返回类型,但由于List<T>
不是懒惰,它不能。将我的返回类型更改为IEnumerable<T>
解决了问题:D
一个有点相关的问题(不值得打开一个新线程):如果我确定99%的情况我要去,那么值得给IEnumerable<T>
作为返回类型.ToList()无论如何?性能影响是什么?
答案 0 :(得分:32)
使用 yield return 的方法必须声明为返回以下两个接口之一:
IEnumerable<SomethingAppropriate>
IEnumerator<SomethingApropriate>
示例:
public IEnumerable<AClass> YourMethod()
{
foreach (XElement header in headersXml.Root.Elements())
{
yield return (ParseHeader(header));
}
}
yield是一个懒惰的数据生成器,只在检索到第一个项目之后生成另一个项目,而返回列表将一次性返回所有项目。
因此存在差异,您需要正确声明方法。
有关详细信息,请阅读Jon's answer here,其中包含一些非常有用的链接。
答案 1 :(得分:15)
这是一个棘手的话题。简而言之,它是实现IEnumerable及其朋友的简单方法。编译器为您构建状态机,将参数和局部变量转换为新类中的实例变量。复杂的东西。
我有一些资源:
答案 2 :(得分:8)
“yield”创建一个迭代器块 - 一个编译器生成的类,可以实现IEnumerable[<T>]
或IEnumerator[<T>]
。 Jon Skeet在C# in Depth的第6章中对此进行了非常好的(并且是免费的)讨论。
但基本上 - 要使用“yield”,您的方法必须返回IEnumerable[<T>]
或IEnumerator[<T>]
。在这种情况下:
public IEnumerable<AClass> SomeMethod() {
// ...
foreach (XElement header in headersXml.Root.Elements()){
yield return (ParseHeader(header));
}
}
答案 3 :(得分:3)
列表实现了Ienumerable。
这是一个可能会对你想要学习的内容有所了解的例子。我写了大约6个月
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace YieldReturnTest
{
public class PrimeFinder
{
private Boolean isPrime(int integer)
{
if (0 == integer)
return false;
if (3 > integer)
return true;
for (int i = 2; i < integer; i++)
{
if (0 == integer % i)
return false;
}
return true;
}
public IEnumerable<int> FindPrimes()
{
int i;
for (i = 1; i < 2147483647; i++)
{
if (isPrime(i))
{
yield return i;
}
}
}
}
class Program
{
static void Main(string[] args)
{
PrimeFinder primes = new PrimeFinder();
foreach (int i in primes.FindPrimes())
{
Console.WriteLine(i);
Console.ReadLine();
}
Console.ReadLine();
Console.ReadLine();
}
}
}
答案 4 :(得分:3)
我强烈建议您使用Reflector查看yield
实际为您做的事情。在使用yield时,您将能够看到编译器为您生成的类的完整代码,并且我发现人们在看到低级别结果时可以更快地理解这个概念(嗯,mid-等级我猜。)
答案 5 :(得分:2)
要了解yield
,您需要了解何时使用IEnumerator
和IEnumerable
(因为您必须使用其中任何一个)。以下示例可帮助您了解其中的差异。
首先,看看下面的类,它实现了两个方法 - 一个返回IEnumerator<int>
,一个返回IEnumerable<int>
。我会告诉你,虽然2种方法的代码看起来很相似但使用方法有很大差异:
// 2 iterators, one as IEnumerator, one as IEnumerable
public class Iterator
{
public static IEnumerator<int> IterateOne(Func<int, bool> condition)
{
for(var i=1; condition(i); i++) { yield return i; }
}
public static IEnumerable<int> IterateAll(Func<int, bool> condition)
{
for(var i=1; condition(i); i++) { yield return i; }
}
}
现在,如果您正在使用IterateOne
,则可以执行以下操作:
// 1. Using IEnumerator allows to get item by item
var i=Iterator.IterateOne(x => true); // iterate endless
// 1.a) get item by item
i.MoveNext(); Console.WriteLine(i.Current);
i.MoveNext(); Console.WriteLine(i.Current);
// 1.b) loop until 100
int j; while (i.MoveNext() && (j=i.Current)<=100) { Console.WriteLine(j); }
1.a)打印:
1
2
1.b)打印:
3
4
...
100
因为它在1.a)语句执行后继续计数。
您可以看到您可以使用MoveNext()
逐项推进。
相比之下,IterateAll
允许您使用foreach
以及 LINQ 语句以获得更大的舒适度:
// 2. Using IEnumerable makes looping and LINQ easier
var k=Iterator.IterateAll(x => x<100); // limit iterator to 100
// 2.a) Use a foreach loop
foreach(var x in k){ Console.WriteLine(x); } // loop
// 2.b) LINQ: take 101..200 of endless iteration
var lst=Iterator.IterateAll(x=>true).Skip(100).Take(100).ToList(); // LINQ: take items
foreach(var x in lst){ Console.WriteLine(x); } // output list
2.a)打印:
1
2
...
99
2.b)打印:
101
102
...
200
注意:由于IEnumerator<T>
和IEnumerable<T>
是泛型,因此可以使用任何类型。但是,为简单起见,我在示例中使用了int
类型T
。
这意味着,您可以使用其中一种返回类型IEnumerator<ProductMixHeader>
或IEnumerable<ProductMixHeader>
(您在问题中提到的自定义类)。
类型List<ProductMixHeader>
未实现任何这些接口,这就是您不能以这种方式使用它的原因。但示例2.b)显示了如何从中创建列表。
如果您通过附加.ToList()
来创建列表,那么暗示是,它将创建内存中所有元素的列表,而IEnumerable
允许延迟创建其元素 - 就性能而言,这意味着元素及时枚举 - 尽可能晚,但只要您使用.ToList()
,就会在内存中创建所有元素。 LINQ尝试在幕后以这种方式优化性能。
<强> DotNetFiddle of all examples 强>
答案 6 :(得分:1)
@Ian P的回答帮助我理解了产量及其使用原因。 yield的一个(主要)用例是在“in”关键字之后的“foreach”循环中,不返回完整列表。而不是一次返回完整列表,在每个“foreach”循环中只返回一个项目(下一个项目)。因此,在这种情况下,您将获得收益率的表现。 我已经重写了@Ian P的代码,以便我更好地理解以下内容:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace YieldReturnTest
{
public class PrimeFinder
{
private Boolean isPrime(int integer)
{
if (0 == integer)
return false;
if (3 > integer)
return true;
for (int i = 2; i < integer; i++)
{
if (0 == integer % i)
return false;
}
return true;
}
public IEnumerable<int> FindPrimesWithYield()
{
int i;
for (i = 1; i < 2147483647; i++)
{
if (isPrime(i))
{
yield return i;
}
}
}
public IEnumerable<int> FindPrimesWithoutYield()
{
var primes = new List<int>();
int i;
for (i = 1; i < 2147483647; i++)
{
if (isPrime(i))
{
primes.Add(i);
}
}
return primes;
}
}
class Program
{
static void Main(string[] args)
{
PrimeFinder primes = new PrimeFinder();
Console.WriteLine("Finding primes until 7 with yield...very fast...");
foreach (int i in primes.FindPrimesWithYield()) // FindPrimesWithYield DOES NOT iterate over all integers at once, it returns item by item
{
if (i > 7)
{
break;
}
Console.WriteLine(i);
//Console.ReadLine();
}
Console.WriteLine("Finding primes until 7 without yield...be patient it will take lonkg time...");
foreach (int i in primes.FindPrimesWithoutYield()) // FindPrimesWithoutYield DOES iterate over all integers at once, it returns the complete list of primes at once
{
if (i > 7)
{
break;
}
Console.WriteLine(i);
//Console.ReadLine();
}
Console.ReadLine();
Console.ReadLine();
}
}
}
答案 7 :(得分:0)
你使用它的方法是什么样的?我不认为这可以单独用于循环。
例如......
public IEnumerable<string> GetValues() {
foreach(string value in someArray) {
if (value.StartsWith("A")) { yield return value; }
}
}