题
在 我怎么能获得只有一个片段IList<> 第一个问题的答复的有下列代码段:
IEnumerable<object> FilteredList()
{
foreach( object item in FullList )
{
if( IsItemInPartialList( item )
yield return item;
}
}
什么不会产率的关键词做吗?我已经看到了它的引用在几个地点和一个其他的问题,但我还没想出什么它实际上做。我已经习惯思维的产意义上的一个线程产生于另一个,但这似乎并没有与此有关。
解决方案
的 yield
关键字实际上做相当多的在这里。
该功能将返回的一个目,实现了 IEnumerable<object>
接口。如果一个叫功能开始 foreach
ing超过这一目的,本功能是再次呼吁,直到它的"产量".这是语法糖介绍了在 C#2.0.在较早的版本你不得不创建自己的 IEnumerable
和 IEnumerator
对象做这样的东西。
最简单的方式了解这样的代码是型的一个例子,设置一些断点看看会发生什么。尝试逐步通过这个例子:
public void Consumer()
{
foreach(int i in Integers())
{
Console.WriteLine(i.ToString());
}
}
public IEnumerable<int> Integers()
{
yield return 1;
yield return 2;
yield return 4;
yield return 8;
yield return 16;
yield return 16777216;
}
当步骤,通过实例中,你会发现第一个呼叫 Integers()
返回 1
.第二次呼吁的回报 2
和线 yield return 1
是不是再次执行。
这里是现实生活中的例子:
public IEnumerable<T> Read<T>(string sql, Func<IDataReader, T> make, params object[] parms)
{
using (var connection = CreateConnection())
{
using (var command = CreateCommand(CommandType.Text, sql, connection, parms))
{
command.CommandTimeout = dataBaseSettings.ReadCommandTimeout;
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
yield return make(reader);
}
}
}
}
}
其他提示
迭代。它创建了一个国家机器"在幕后",记得你在哪儿上的每一个额外的周期的功能并选择从那里。
最近的雷蒙德陈还跑了一个有趣的一系列条款对产率的关键词。
虽然这是名义上用于轻易地实行一个迭代的模式,但可以推广到国家机器。有点中引用的雷蒙德的最后一部分还链接到其它用途(但如在Entin的博客esp良好,显示出如何编写异步的安全代码)。
乍一看,返回收率。净糖返回的一个类型.
没有收率,所有项目的收集创建一次:
class SomeData
{
public SomeData() { }
static public IEnumerable<SomeData> CreateSomeDatas()
{
return new List<SomeData> {
new SomeData(),
new SomeData(),
new SomeData()
};
}
}
相同的代码使用率,它返回项目的项目:
class SomeData
{
public SomeData() { }
static public IEnumerable<SomeData> CreateSomeDatas()
{
yield return new SomeData();
yield return new SomeData();
yield return new SomeData();
}
}
使用率,如果能消耗的数据只需要第一个项目的收集,其余的项目将不会被创建。
产率的操作者允许建立项目,因为它被要求。这是一个很好的理由去使用它。
yield return
使用与调查员.在每个电话的收率的发言,控制返回给来电,但是它确保被叫方的国家是维持。由于这个原因,当呼叫者列举了下一个元素,它继续执行在被叫方法,从声明后,立即 yield
发言。
让我们试图理解这样一个例子。在这个例子中,对应于每一行我已经提到了在执行流动。
static void Main(string[] args)
{
foreach (int fib in Fibs(6))//1, 5
{
Console.WriteLine(fib + " ");//4, 10
}
}
static IEnumerable<int> Fibs(int fibCount)
{
for (int i = 0, prevFib = 0, currFib = 1; i < fibCount; i++)//2
{
yield return prevFib;//3, 9
int newFib = prevFib + currFib;//6
prevFib = currFib;//7
currFib = newFib;//8
}
}
此外,国家保持对每一枚举。假设,我有另一个电话来 Fibs()
方法,那么国家将须重置。
一个列表执行负载的所有项目立即而产率的实现提供了一个延迟执行解决方案。
在实践中,常常需要执行的最低数额的工作需要的,以便减少资源消耗的一个应用程序。
例如,我们可能有一个应用程序,处理数以百万计的记录,从一个数据库。以下好处,可以实现,当我们使用综合在一个延迟执行基于请求的模型:
- 可伸缩性、可靠性和可预测性 都可能改善由于记录的数量并不明显影响的应用程序的资源要求。
- 性能和反应能力 都可能改善由于处理可以立即开始,而不是等待整个集合被载入第一次。
- 可恢复性和利用率 都可能改善由于应用程序可以被停止,开始、中断或故障。只有项目的进展将会丢失相比,预先提取的所有数据,其中只使用一部分的结果是实际上使用。
- 连续的处理 是的可能在环境中的工作量不断流增加。
这里是一个比较之间建立一个收集第一个这样的因列表相比,使用率。
例列表
public class ContactListStore : IStore<ContactModel>
{
public IEnumerable<ContactModel> GetEnumerator()
{
var contacts = new List<ContactModel>();
Console.WriteLine("ContactListStore: Creating contact 1");
contacts.Add(new ContactModel() { FirstName = "Bob", LastName = "Blue" });
Console.WriteLine("ContactListStore: Creating contact 2");
contacts.Add(new ContactModel() { FirstName = "Jim", LastName = "Green" });
Console.WriteLine("ContactListStore: Creating contact 3");
contacts.Add(new ContactModel() { FirstName = "Susan", LastName = "Orange" });
return contacts;
}
}
static void Main(string[] args)
{
var store = new ContactListStore();
var contacts = store.GetEnumerator();
Console.WriteLine("Ready to iterate through the collection.");
Console.ReadLine();
}
控制台输出
ContactListStore:建立联系1
ContactListStore:建立联系2
ContactListStore:建立联系3
准备来循环的集合。
注:整个集装入存储器,甚至没有要求为一个单一的项目列表中的
产量的例子
public class ContactYieldStore : IStore<ContactModel>
{
public IEnumerable<ContactModel> GetEnumerator()
{
Console.WriteLine("ContactYieldStore: Creating contact 1");
yield return new ContactModel() { FirstName = "Bob", LastName = "Blue" };
Console.WriteLine("ContactYieldStore: Creating contact 2");
yield return new ContactModel() { FirstName = "Jim", LastName = "Green" };
Console.WriteLine("ContactYieldStore: Creating contact 3");
yield return new ContactModel() { FirstName = "Susan", LastName = "Orange" };
}
}
static void Main(string[] args)
{
var store = new ContactYieldStore();
var contacts = store.GetEnumerator();
Console.WriteLine("Ready to iterate through the collection.");
Console.ReadLine();
}
控制台输出
准备来循环的集合。
注:该集合不是执行。这是由于"推迟执行"性质的类型.构建一个项目将只发生当它是真的需要。
让我们打电话的收集和再次正面的行为时,我们获取的第一联系在收集。
static void Main(string[] args)
{
var store = new ContactYieldStore();
var contacts = store.GetEnumerator();
Console.WriteLine("Ready to iterate through the collection");
Console.WriteLine("Hello {0}", contacts.First().FirstName);
Console.ReadLine();
}
控制台输出
准备来循环的集合
ContactYieldStore:建立联系1
你好鲍勃
好!只有第一次接触是建造时的客户"拉"项目的收集。
这里是一个简单的方式来理解这个概念:基本的想法是,如果你想要收集你可以使用"foreach
"在,但收集的项目进入收集是昂贵的,因为某些原因(如查询他们的数据库),和你常常会不需要整个集合,然后创建一个功能,建立收集的一个项目的时间,并产生到消费者(人然后可以终止收集工作的初期).
它认为这种方式: 你去肉柜台,并希望购买一磅火腿切片.屠夫需要一个10磅火腿到后面,把它放在切片机片的整个事情,然后带来一堆片,回到你和措施出一磅。(旧的方式)。与 yield
, 屠夫带来了切片机以反,并启动切片和"产出"的每个片上规模,直到它措施的1磅重的,那么包裹给你和你完成。 旧的方式可以是更好的屠夫(让他组织他机械的方式,他喜欢),但新的方式显然是更有效率在大多数情况下,对消费者。
的 yield
关键词,您可以创建 IEnumerable<T>
在形式上 迭代块.这个迭代的框支持 推迟执行 如果你不熟悉的概念很可能出现的几乎不可思议的。然而,在一天结束,它仅仅是执行的代码没有任何怪异的技巧。
一个迭代块可以被描述为法糖在哪里编译器生成的一个国家机器,用于跟踪多远枚举的可枚举已经取得了进展。列举的可枚举,往往使用 foreach
循环。然而,一个 foreach
循环,也是语法糖。所以你是两个抽象,从真正的代码这就是为什么它最初可能很难了解它是如何工作在一起。
假设你有一个非常简单的迭代块:
IEnumerable<int> IteratorBlock()
{
Console.WriteLine("Begin");
yield return 1;
Console.WriteLine("After 1");
yield return 2;
Console.WriteLine("After 2");
yield return 42;
Console.WriteLine("End");
}
真正的迭代块经常有条件和环但是当你检查的条件和展开的循环,他们仍然结束了 yield
报表交织与其他代码。
列举的迭代方块a foreach
循环使用:
foreach (var i in IteratorBlock())
Console.WriteLine(i);
这里是输出(没有惊喜):
Begin 1 After 1 2 After 2 42 End
如上所述 foreach
是语法糖:
IEnumerator<int> enumerator = null;
try
{
enumerator = IteratorBlock().GetEnumerator();
while (enumerator.MoveNext())
{
var i = enumerator.Current;
Console.WriteLine(i);
}
}
finally
{
enumerator?.Dispose();
}
在一个试图解开这个我已经装箱的顺序图与抽象删除:
国家机所产生的编译器也实现了枚举,但使图更清楚,我已显示出它们作为单独的实例。(当的国家机是列举从另外一个线程你做实际上获得独立的实例,但这一细节是不重要的在这里.)
你每次呼叫你迭代方框一个新的实例的国家机是创建。然而,没有你的代码中的迭代方框执行,直到 enumerator.MoveNext()
执行的第一次。这是如何延迟执行工作。这里是一个(而不是愚蠢的)例如:
var evenNumbers = IteratorBlock().Where(i => i%2 == 0);
在这一点的迭代没有执行。的 Where
条款,创建一个新的 IEnumerable<T>
那个包裹 IEnumerable<T>
返回的 IteratorBlock
但是,这枚举尚未一一列举。这种情况发生在你执行 foreach
循环:
foreach (var evenNumber in evenNumbers)
Console.WriteLine(eventNumber);
如果你列举的可枚举的两倍,然后,一个新的实例的国家机创造了每个时间和你迭代的框会执行同样的代码的两倍。
注意到皇宫的方法一样 ToList()
, ToArray()
, First()
, Count()
等等。将使用一个 foreach
环列举的可枚举的。例如 ToList()
会列举的所有要素可枚举,并将它们存储在一个列表。你现在可以访问的名单得到的所有要素。没有迭代的框执行。有一个贸易之间使用CPU生产要素的可枚举的多次和存储器中存储的元素枚举的到访问他们多次当使用的方法一样 ToList()
.
如果我理解正确,这是我如何将这一短语从这个角度的功能实施综合与率。
- 这里有一个。
- 再打电话如果你需要另一个。
- 我记得我已经给了你。
- 我只知道如果我可以给你另一当你再打电话。
C#产率的关键词,简单地说,允许许多电话,以代码体,称为一个迭代,知道如何返回之前的工作和时再次呼吁,继续在它离开-即它可以帮助一个迭代变得透明状态的每个项目的顺序的迭代的回返,在连续呼吁。
在JavaScript,同样的概念被称为发电机。
这是一个非常简单和容易的方式来创建一个可枚举,对你的对象。编译器创建一类包装的方法和实现,在这种情况下,综合<object>.没有产率的关键词,你就必须创建一个对象,实施类型<object>.
它是产生可枚举的顺序。什么,它实际上是建立当地类型的顺序和返回作为一种方法的结果
此 链接 有一个简单的例子
甚至更简单的例子在这里
public static IEnumerable<int> testYieldb()
{
for(int i=0;i<3;i++) yield return 4;
}
注意到返回收率不会回来的方法。你甚至可以把一个 WriteLine
后 yield return
以上产生了一个综合的4整数4,4,4,4
这里有一个 WriteLine
.将增加4到列表,abc打印,然后添加4到列表,然后完成的方法和所以,真正的返回的方法(一的方法已完成,作为会发生一个过程而不返回)。但是,这将具有的价值, IEnumerable
列表 int
s,它返回上完成。
public static IEnumerable<int> testYieldb()
{
yield return 4;
console.WriteLine("abc");
yield return 4;
}
还请注意,在使用的产率,什么你回来的是不同类型的功能。它是类型的内的元素 IEnumerable
名单。
你使用率的方法是返回的类型 IEnumerable
.如果方法的回报类型 int
或 List<int>
和你使用 yield
, 然后它不会编译。你可以使用 IEnumerable
法返回的类型没有收率,但它似乎也许你可以不使用率没有 IEnumerable
法返回的类型。
并得到它执行你必须呼吁它在一个特殊的方式。
static void Main(string[] args)
{
testA();
Console.Write("try again. the above won't execute any of the function!\n");
foreach (var x in testA()) { }
Console.ReadLine();
}
// static List<int> testA()
static IEnumerable<int> testA()
{
Console.WriteLine("asdfa");
yield return 1;
Console.WriteLine("asdf");
}
它试图带来一些红宝石善良:)
概念: 这是一些样品红宝石的代码打印出每一个元件阵列
rubyArray = [1,2,3,4,5,6,7,8,9,10]
rubyArray.each{|x|
puts x # do whatever with x
}
该阵列的每个方法的执行情况 产量 控制的呼叫者(的'把x')与 每 元件阵列整齐地列为x。呼叫者然后可以做任何事需要做x。
然而 .净 不是所有的方式在这里..C#似乎已经联接的收益率与综合的方式迫使你写foreach循环的呼叫者如Mendelt的响应。一点小优雅。
//calling code
foreach(int i in obCustomClass.Each())
{
Console.WriteLine(i.ToString());
}
// CustomClass implementation
private int[] data = {1,2,3,4,5,6,7,8,9,10};
public IEnumerable<int> Each()
{
for(int iLooper=0; iLooper<data.Length; ++iLooper)
yield return data[iLooper];
}