https://ayende.com/blog/3082/pipes-and-filters-the-ienumerable-appraoch代码
问题有三个已注册的操作。首先获取系统中的所有进程。第二个过滤器处理。第三,写进程名。
但使用的是产量和GetEnumerator。
行current = operation.Execute(current);被执行三次,第一次操作的输出列表是第二次操作的输入。而且它只是一个列表(操作)。我不明白如何和什么存储在private readonly List<IOperation<T>> operations = new List<IOperation<T>>();?因为他有三次死刑。执行enumerator.MoveNext()时执行的某种委托?好吧,但是我们有Foreach,哪个被执行了?在使用GetEnumerator之前三次。因此,列出“操作”应该是ovverriden .?:)我想了解一种机制。在使用enumerator.MoveNext()之前,如何存储这个“委托”。是怎么用的呢?
class Program
{
static void Main(string[] args)
{
var trivialProcess = new Pipeline<Process>();
trivialProcess.Register(new GetAllProcesses());
trivialProcess.Register(new LimitByWorkingSetSize());
trivialProcess.Register(new PrintProcessName());
trivialProcess.Execute();
}
}
interface IOperation<T>
{
IEnumerable<T> Execute(IEnumerable<T> input);
}
class GetAllProcesses : IOperation<Process>
{
public IEnumerable<Process> Execute(IEnumerable<Process> input)
{
Debug.WriteLine("GetAllProcesses Execute");
return Process.GetProcesses();
}
}
class LimitByWorkingSetSize : IOperation<Process>
{
public IEnumerable<Process> Execute(IEnumerable<Process> input)
{
int maxSizeBytes = 50 * 1024 * 1024;
Debug.WriteLine("LimitByWorkingSetSize Enter");
foreach (Process process in input)
{
Debug.WriteLine("LimitByWorkingSetSize foreach");
if (process.WorkingSet64 > maxSizeBytes)
{
Debug.WriteLine("LimitByWorkingSetSize yield");
yield return process;
}
}
}
}
class PrintProcessName : IOperation<Process>
{
public IEnumerable<Process> Execute(IEnumerable<Process> input)
{
Debug.WriteLine("PrintProcessName Enter");
foreach (Process process in input)
{
Debug.WriteLine("PrintProcessName print");
Console.WriteLine(process.ProcessName);
}
Debug.WriteLine("PrintProcessName break");
yield break;
}
}
class Pipeline<T>
{
private readonly List<IOperation<T>> operations = new List<IOperation<T>>();
public Pipeline<T> Register(IOperation<T> operation)
{
operations.Add(operation);
return this;
}
public void Execute()
{
IEnumerable<T> current = new List<T>();
foreach (IOperation<T> operation in operations)
{
current = operation.Execute(current);
}
IEnumerator<T> enumerator = current.GetEnumerator();
while (enumerator.MoveNext()) ;
}
}发布于 2022-03-15 21:24:33
我不明白私有只读列表操作=新列表();??是如何存储的?
它是IOperation<T>的列表,在本例中是GetAllProcesses、LimitByWorkingSetSize或PrintProcessName类的实例。所有这些类都实现了IOperation<T>接口,因此能够作为IOperation<T>来处理,并且允许存储在列表中。
作为foreach有三个执行
是的,但这只是为LimitBy和PrintProcesses类设置调用链,因为它们的Execute是生成器(Featureyield)--只有在枚举它们时才会执行它们。current = operation.Execute(current);将立即执行Execute of GetAll,因为这不会导致任何事情(但是它可以被修改),但是在PrintProcess中运行代码,这依赖于LimitBy,只有在发生MoveNext()时才会发生。PrintProcess陷阱控制流,在枚举时不使用yield。
在执行
()时执行的enumerator.MoveNext委托?
我怀疑这是在深入研究这方面的收益机制(但你在下面再问一次,所以..)
好的,但是我们有Foreach,哪个是执行的?
在给出的代码示例中有很多foreaches。执行PrintProcess的Execute运行一个foreach,它枚举链中下一个方法的输出(LimitBy的Execute) --它本身也包含一个foreach。如果您在调试器中单步执行它,在每个{下都有一个断点,那么您将看到它在PrintProcess和LimitBy之间来回跳跃;LimitBy正在生成PrintProcess正在枚举的某些循环传递的数据。PrintProcesses不对进一步向上的操作执行任何操作,因此控制在这两者之间来回传递,直到LimitBy没有任何东西可以给出为止,此时PrintProcess将不再需要枚举任何东西。
,所以列出“操作”应该是不可能的……?
我怀疑这是一种被改写的错误。代码中没有覆盖operations列表的任何内容;重写的是current。它只是一种记忆最后一次输出的方式,因此它可以在循环的每一次传递中成为一个输入。从一开始就没有必要将其插入到新的List中,因为这个链中的第一个Execute对它的input没有任何作用。
这个“委托”是如何在使用enumerator.MoveNext()之前存储的。是怎么用的呢?
我认为您实际上是在问yield是如何工作的;简短的版本是编译器为您编写了一些类,这些类实现了基于围绕yield关键字使用的逻辑生成值的枚举器。很长的答案是相当可怕的,而不是你自己想要写的东西,这就是为什么我们通常非常乐意让编译器生成它,而不去看下面的内容:
#define DEBUG
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
namespace ConsoleApp7net5
{
internal class Program
{
private static void Main(string[] args)
{
Pipeline<Process> pipeline = new Pipeline<Process>();
pipeline.Register(new GetAllProcesses());
pipeline.Register(new LimitByWorkingSetSize());
pipeline.Register(new PrintProcessName());
pipeline.Execute();
}
}
internal interface IOperation<T>
{
IEnumerable<T> Execute(IEnumerable<T> input);
}
internal class GetAllProcesses : IOperation<Process>
{
public IEnumerable<Process> Execute(IEnumerable<Process> input)
{
Debug.WriteLine("GetAllProcesses Execute");
return Process.GetProcesses();
}
}
internal class LimitByWorkingSetSize : IOperation<Process>
{
[CompilerGenerated]
private sealed class <Execute>d__0 : IEnumerable<Process>, IEnumerable, IEnumerator<Process>, IDisposable, IEnumerator
{
private int <>1__state;
private Process <>2__current;
private int <>l__initialThreadId;
private IEnumerable<Process> input;
public IEnumerable<Process> <>3__input;
public LimitByWorkingSetSize <>4__this;
private int <maxSizeBytes>5__1;
private IEnumerator<Process> <>s__2;
private Process <process>5__3;
Process IEnumerator<Process>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <Execute>d__0(int <>1__state)
{
this.<>1__state = <>1__state;
<>l__initialThreadId = Environment.CurrentManagedThreadId;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
int num = <>1__state;
if (num == -3 || num == 1)
{
try
{
}
finally
{
<>m__Finally1();
}
}
}
private bool MoveNext()
{
try
{
int num = <>1__state;
if (num != 0)
{
if (num != 1)
{
return false;
}
<>1__state = -3;
goto IL_00bb;
}
<>1__state = -1;
<maxSizeBytes>5__1 = 52428800;
Debug.WriteLine("LimitByWorkingSetSize Enter");
<>s__2 = input.GetEnumerator();
<>1__state = -3;
goto IL_00c3;
IL_00bb:
<process>5__3 = null;
goto IL_00c3;
IL_00c3:
if (<>s__2.MoveNext())
{
<process>5__3 = <>s__2.Current;
Debug.WriteLine("LimitByWorkingSetSize foreach");
if (<process>5__3.WorkingSet64 > <maxSizeBytes>5__1)
{
Debug.WriteLine("LimitByWorkingSetSize yield");
<>2__current = <process>5__3;
<>1__state = 1;
return true;
}
goto IL_00bb;
}
<>m__Finally1();
<>s__2 = null;
return false;
}
catch
{
//try-fault
((IDisposable)this).Dispose();
throw;
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
private void <>m__Finally1()
{
<>1__state = -1;
if (<>s__2 != null)
{
<>s__2.Dispose();
}
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
[DebuggerHidden]
IEnumerator<Process> IEnumerable<Process>.GetEnumerator()
{
<Execute>d__0 <Execute>d__;
if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
{
<>1__state = 0;
<Execute>d__ = this;
}
else
{
<Execute>d__ = new <Execute>d__0(0);
<Execute>d__.<>4__this = <>4__this;
}
<Execute>d__.input = <>3__input;
return <Execute>d__;
}
[DebuggerHidden]
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable<Process>)this).GetEnumerator();
}
}
[IteratorStateMachine(typeof(<Execute>d__0))]
public IEnumerable<Process> Execute(IEnumerable<Process> input)
{
<Execute>d__0 <Execute>d__ = new <Execute>d__0(-2);
<Execute>d__.<>4__this = this;
<Execute>d__.<>3__input = input;
return <Execute>d__;
}
}
internal class PrintProcessName : IOperation<Process>
{
[CompilerGenerated]
private sealed class <Execute>d__0 : IEnumerable<Process>, IEnumerable, IEnumerator<Process>, IDisposable, IEnumerator
{
private int <>1__state;
private Process <>2__current;
private int <>l__initialThreadId;
private IEnumerable<Process> input;
public IEnumerable<Process> <>3__input;
public PrintProcessName <>4__this;
private IEnumerator<Process> <>s__1;
private Process <process>5__2;
Process IEnumerator<Process>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <Execute>d__0(int <>1__state)
{
this.<>1__state = <>1__state;
<>l__initialThreadId = Environment.CurrentManagedThreadId;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
}
private bool MoveNext()
{
if (<>1__state != 0)
{
return false;
}
<>1__state = -1;
Debug.WriteLine("PrintProcessName Enter");
<>s__1 = input.GetEnumerator();
try
{
while (<>s__1.MoveNext())
{
<process>5__2 = <>s__1.Current;
Debug.WriteLine("PrintProcessName print");
Console.WriteLine(<process>5__2.ProcessName);
<process>5__2 = null;
}
}
finally
{
if (<>s__1 != null)
{
<>s__1.Dispose();
}
}
<>s__1 = null;
Debug.WriteLine("PrintProcessName break");
return false;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
[DebuggerHidden]
IEnumerator<Process> IEnumerable<Process>.GetEnumerator()
{
<Execute>d__0 <Execute>d__;
if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
{
<>1__state = 0;
<Execute>d__ = this;
}
else
{
<Execute>d__ = new <Execute>d__0(0);
<Execute>d__.<>4__this = <>4__this;
}
<Execute>d__.input = <>3__input;
return <Execute>d__;
}
[DebuggerHidden]
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable<Process>)this).GetEnumerator();
}
}
[IteratorStateMachine(typeof(<Execute>d__0))]
public IEnumerable<Process> Execute(IEnumerable<Process> input)
{
<Execute>d__0 <Execute>d__ = new <Execute>d__0(-2);
<Execute>d__.<>4__this = this;
<Execute>d__.<>3__input = input;
return <Execute>d__;
}
}
internal class Pipeline<T>
{
private readonly List<IOperation<T>> operations = new List<IOperation<T>>();
public Pipeline<T> Register(IOperation<T> operation)
{
operations.Add(operation);
return this;
}
public void Execute()
{
IEnumerable<T> enumerable = null;
List<IOperation<T>>.Enumerator enumerator = operations.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
IOperation<T> current = enumerator.Current;
enumerable = current.Execute(enumerable);
}
}
finally
{
((IDisposable)enumerator).Dispose();
}
IEnumerator<T> enumerator2 = enumerable.GetEnumerator();
while (enumerator2.MoveNext())
{
int num = 0;
}
}
}
}为了我的钱,我可能会把这一切都置之不理,只管这么做:
Process.GetProcesses()
.Where(p => p.BasePriority > 50*1024*1024)
.Select(p => p.ProcessName)
.ToList();这是关于yield的一点
当一个方法使用yield返回某些内容时,就可以回到该方法中,并从我们停止的地方继续下去。看看这个不会产生结果的方法:
int FibonacciSequence(){
return 1;
return 2;
return 3;
return 5;
}当我们调用它时,我们只会得到1。第一次返回是一个艰难的停止;我们可以称它一百万次,只有第一次返回才能做任何事情。所有其他代码都是完全无法到达的。
现在,让我们修改它,使其产生如下结果:
IEnumerable<int> FibonacciSequence(){
yield return 1;
yield return 2;
yield return 3;
yield return 5;
}它现在是一个可枚举的,所以现在我们将其称为将对其进行枚举的部分:
foreach(var v in FibonacciSequence())
Console.WriteLine(v);编译器做了很多事情来将您的foreach转换成一个枚举器,并一次又一次地调用movenext,但关键是,当您枚举这个生成方法时--它
回传1到printed
H 268>H 169>3被打印出来<代码>H 270<代码>H 171返回,返回5H 272<代码>H 173打印5<代码>H 274/代码><>H 175回到方法的末尾,停止<枚举代码H><>因此,当我们屈服时,就像它在方法中放置了一个标记,返回到调用者,当我们回到方法中(沿着一个移动迭代器)时,我们从停止的标记开始,而不是从方法的第一行开始。
您不必硬编码您的结果;您可以编写一个可以永远枚举的方法:
IEnumerable<int> FibonacciSequence(){
int prevprev = 0;
int prev = 1;
while(true){
int toReturn = prevprev + prev;
prevprev = prev;
prev = toReturn;
yield return toReturn;
}
}每次访问yield时,C#都会返回到带有新值的枚举代码。因为这是在一个循环中,它只会一直生成斐波纳契,直到它爆炸(如果你已经检查了数学,或者你填满了你的记忆,那么这个加法就会导致溢出。)或者,如果你只是在印刷,它就会永远消失)。要停止枚举,要么需要从方法返回而不产生结果(从方法的末尾掉下来),要么执行yield break
https://stackoverflow.com/questions/71487917
复制相似问题