首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >IEnumerable流水线

IEnumerable流水线
EN

Stack Overflow用户
提问于 2022-03-15 19:26:56
回答 1查看 113关注 0票数 -1

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()之前,如何存储这个“委托”。是怎么用的呢?

代码语言:javascript
复制
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()) ;
        }
    }
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-03-15 21:24:33

我不明白私有只读列表操作=新列表();??是如何存储的?

它是IOperation<T>的列表,在本例中是GetAllProcessesLimitByWorkingSetSizePrintProcessName类的实例。所有这些类都实现了IOperation<T>接口,因此能够作为IOperation<T>来处理,并且允许存储在列表中。

作为foreach有三个执行

是的,但这只是为LimitByPrintProcesses类设置调用链,因为它们的Execute是生成器(Featureyield)--只有在枚举它们时才会执行它们。current = operation.Execute(current);将立即执行Execute of GetAll,因为这不会导致任何事情(但是它可以被修改),但是在PrintProcess中运行代码,这依赖于LimitBy,只有在发生MoveNext()时才会发生。PrintProcess陷阱控制流,在枚举时不使用yield

在执行

()时执行的enumerator.MoveNext委托?

我怀疑这是在深入研究这方面的收益机制(但你在下面再问一次,所以..)

好的,但是我们有Foreach,哪个是执行的?

在给出的代码示例中有很多foreaches。执行PrintProcess的Execute运行一个foreach,它枚举链中下一个方法的输出(LimitByExecute) --它本身也包含一个foreach。如果您在调试器中单步执行它,在每个{下都有一个断点,那么您将看到它在PrintProcessLimitBy之间来回跳跃;LimitBy正在生成PrintProcess正在枚举的某些循环传递的数据。PrintProcesses不对进一步向上的操作执行任何操作,因此控制在这两者之间来回传递,直到LimitBy没有任何东西可以给出为止,此时PrintProcess将不再需要枚举任何东西。

,所以列出“操作”应该是不可能的……?

我怀疑这是一种被改写的错误。代码中没有覆盖operations列表的任何内容;重写的是current。它只是一种记忆最后一次输出的方式,因此它可以在循环的每一次传递中成为一个输入。从一开始就没有必要将其插入到新的List中,因为这个链中的第一个Execute对它的input没有任何作用。

这个“委托”是如何在使用enumerator.MoveNext()之前存储的。是怎么用的呢?

我认为您实际上是在问yield是如何工作的;简短的版本是编译器为您编写了一些类,这些类实现了基于围绕yield关键字使用的逻辑生成值的枚举器。很长的答案是相当可怕的,而不是你自己想要写的东西,这就是为什么我们通常非常乐意让编译器生成它,而不去看下面的内容:

代码语言:javascript
复制
#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;
            }
        }
    }
}

为了我的钱,我可能会把这一切都置之不理,只管这么做:

代码语言:javascript
复制
Process.GetProcesses()
  .Where(p => p.BasePriority > 50*1024*1024)
  .Select(p => p.ProcessName)
  .ToList();

这是关于yield的一点

当一个方法使用yield返回某些内容时,就可以回到该方法中,并从我们停止的地方继续下去。看看这个不会产生结果的方法:

代码语言:javascript
复制
int FibonacciSequence(){
  return 1;
  return 2;
  return 3;
  return 5;
}

当我们调用它时,我们只会得到1。第一次返回是一个艰难的停止;我们可以称它一百万次,只有第一次返回才能做任何事情。所有其他代码都是完全无法到达的。

现在,让我们修改它,使其产生如下结果:

代码语言:javascript
复制
IEnumerable<int> FibonacciSequence(){
  yield return 1;
  yield return 2;
  yield return 3;
  yield return 5;
}

它现在是一个可枚举的,所以现在我们将其称为将对其进行枚举的部分:

代码语言:javascript
复制
foreach(var v in FibonacciSequence())
  Console.WriteLine(v);

编译器做了很多事情来将您的foreach转换成一个枚举器,并一次又一次地调用movenext,但关键是,当您枚举这个生成方法时--它

回传1到printed

  • movenext循环

  • 1是由foreach隐式调用的,这使得c#在“返回1”之后返回到方法中,而这一次它返回2

  • 2是打印

  • 返回到方法中,返回3H 268>H 169>3被打印出来<代码>H 270<代码>H 171返回,返回5H 272<代码>H 173打印5<代码>H 274/代码><>H 175回到方法的末尾,停止<枚举代码H><>

因此,当我们屈服时,就像它在方法中放置了一个标记,返回到调用者,当我们回到方法中(沿着一个移动迭代器)时,我们从停止的标记开始,而不是从方法的第一行开始。

您不必硬编码您的结果;您可以编写一个可以永远枚举的方法:

代码语言:javascript
复制
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

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71487917

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档