当前位置: 首页 > 编程语言 > C#

有没有只迭代其源码一次的IEnumerable实现(如LINQ)分享

时间:2023-04-11 01:57:18 C#

表达式:varitems=fromiteminItemsSource.RetrieveItems()where...假定每个项目的生成都需要一些不可忽略的时间。有两种操作模式:使用foreach将允许开始使用集合开头的项目,而不是最终可用的项目。但是,如果我们以后想再次处理同一个集合,我们将不得不复制保存它:varstoredItems=newList();foreach(项目中的变量项目){过程(项目);storedItems.Add(项目);}//稍后foreach(variteminstoredItems){ProcessMore(item);因为如果我们只是执行foreach(...initems)那么temsSource.RetrieveItems()将被再次调用。我们可以预先使用.ToList(),但这会迫使我们在开始处理第一项之前等待检索最后一项。问题:是否有一个IEnumerable实现会像常规LINQ查询结果一样第一次迭代,但会在进程中实现,以便第二个foreach会迭代存储的值?一个有趣的挑战,所以我必须提供自己的解决方案。事实上,我的解决方案现在很有趣,因为版本3。版本2是我根据Servy的反馈所做的简化。然后我意识到我的解决方案有一个很大的缺点。如果缓存的可枚举的第一次枚举不完整,则不会发生缓存。许多LINQ扩展,如First和Take,只会枚举足够的可枚举来完成工作,我必须更新到版本3才能让它与缓存一起工作。问题是关于可枚举的后续枚举,它不涉及并发访问。但是我决定让我的解决方案线程安全。它增加了一些复杂性和一些开销,但应该允许在所有场景中使用该解决方案。publicstaticclassEnumerableExtensions{publicstaticIEnumerableCached(thisIEnumerablesource){if(source==null)thrownewArgumentNullException("source");}返回新的缓存枚举(源);}}类CachedEnumerable:IEnumerable{readonlyObjectgate=newObject();只读IEnumerable源;只读列表缓存=新列表();IEnumerator枚举器;boolisCacheComplete;publicCachedEnumerable(IEnumerablesource){this.source=source;}publicIEnumeratorGetEnumerator(){lock(this.gate){if(this.isCacheComplete)returnthis.cache.GetEnumerator();如果(this.enumerator==null)this.enumerator=source.GetEnumerator();}返回GetCacheBuildingEnumerator();}publicIEnumeratorGetCacheBuildingEnumerator(){varindex=0;T项;while(TryGetItem(index,outitem)){yieldreturnitem;索引+=1;}}boolTryGetItem(Int32index,outTitem){lock(this.gate){if(!IsItemInCache(index)){//迭代可能有comp在等待锁的时候让。如果(this.isCacheComplete){item=default(T);返回假;}if(!this.enumerator.MoveNext()){item=default(T);this.isCacheComplete=true;this.enumerator.Dispose();返回假;}this.cache.Add(this.enumerator.Current);}item=this.cache[index];返回真;}}boolIsItemInCache(Int32index){returnindexextended名称使用如下(sequence为IEnumerable):varcachedSequence=sequence.Cached();//从序列中拉取2个项目。foreach(varitemincachedSequence.Take(2))//...//从缓存中提取2个项目,从源中提取其余项目。foreach(varitemincachedSequence)//...//从缓存中提取所有项目。foreach(varitemincachedSequence)//...如果枚举的一部分是枚举枚举,会有轻微的泄漏(例如cachedSequence.Take(2).ToList()ToList使用的枚举器将被cachedSequence.Take(2).ToList()但底层源枚举器不会被丢弃。这是因为前2个项目被缓存,如果对后续项目的请求被缓存,源枚举器将保持活动状态。在这种情况下,源枚举器仅被清除当它容易进行垃圾收集时(与潜在的大缓存并发)。查看ReactiveExtensions库-有一个MemoizeAll()扩展,它将在访问IEnumerable后缓存项目,并存储它们以供将来访问。请参阅BartDeSmetMemoizeAll的这篇博文,了解MemoizeAll和其他Rx方法。编辑:这实际上现在可以在单独的交互式扩展包中使用-可从NuGet或MicrosoftDownload获得。publicstaticIEnumerableSingleEnumeration(这个IEnumerable源){returnnewSingleEnumerator(源);}privateclassSingleEnumerator:IEnumerable{privateCacheEntrycacheEntry;publicSingleEnumerator(IEnumerablesequence){cacheEntry=newCacheEntry(sequence.GetEnumerator());}publicIEnumeratorGetEnumerator(){if(cacheEntry.FullyPopulated){返回cacheEntry.CachedValues.GetEnumerator();}else{returniterateSequence(cacheEntry).GetEnumerator();}}IEnumeratorIEnumerable.GetEnumerator(){返回this.GetEnumerator();}}privatestaticIEnumerableiterateSequence(CacheEntryentry){使用(variterator=entry.CachedValues.GetEnumerator()){inti=0;while(entry.ensureItemAt(i)&&iterator.MoveNext()){yieldreturniterator.Current;我++;}}}privateclassCacheEntry{publicboolFullyPopulated{get;私有集;}publicConcurrentQueueCachedValues{get;私有集;}privatestaticobject键=newobjec吨();私有IEnumerator序列;publicCacheEntry(IEnumeratorsequence){this.sequence=sequence;CachedValues=newConcurrentQueue();}//////确保缓存中有提供的索引项。如果不是,则从///输入序列中取出一个项目并移至缓存。//////该方法是线程安全的。//////如果缓存已经有足够的项目或///一个项目被移动到缓存,则为真,///如果序列中没有更多项目,则为假。publicboolensureItemAt(intindex){//如果缓存中已经有我们不需要锁定的项目//我们可以得到它if(index所以这已经被编辑(基本上)以支持多线程访问,其中多个线程可以requestitem,itembyitem,theywillbecached.不需要等待整个序列被迭代它返回缓存的值。下面的示例程序演示了这一点:privatestaticIEnumerableinterestingIntGenerationMethod(intmaxValue){for(inti=0;isequence=interestingIntGenerationMethod(10).SingleEnumeration();intnumThreads=3;for(inti=0;i{foreach(intvalueinsequence){Console.WriteLine("Task:{0}Value:{1}",taskID,value);}});}Console.WriteLine("Pressanykeytoexit...");Console.ReadKey(true);}你真的需要看到它才能理解这里的力量。一旦一个线程强制生成下一个实际值,所有剩余的线程都可以立即打印那个生成的值,但是如果没有未缓存的值thread,theywillallwait.(显然thread/threadpoolschedulingmaycauseatasktotakelongertoprintitsvaluethanneeded.)以上是C#学习教程:IstheretheIEnumerableimplementationthatonlyiteratesItssource(suchasLINQ)一次性把所有内容分享出来,如果对你有用,需要了解更多C#学习教程,希望大家多多关注。本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处: