当前位置: 首页 > news >正文

给静态网站加后台网站建设的结尾

给静态网站加后台,网站建设的结尾,3免费做网站,wordpress在什么系统下搭建匹夫细说C##xff1a;庖丁解牛迭代器#xff0c;那些藏在幕后的秘密c#语言规范阅读目录0x00 前言0x01 你好#xff0c;迭代器0x02 原来是状态机呀0x03 状态管理0x04 总结回到目录0x00 前言在匹夫的上一篇文章《匹夫细说C##xff1a;不是“栈类型”的值类型#xff0c;从…匹夫细说C#庖丁解牛迭代器那些藏在幕后的秘密c#语言规范阅读目录0x00 前言0x01 你好迭代器0x02 原来是状态机呀0x03 状态管理0x04 总结回到目录0x00 前言在匹夫的上一篇文章《匹夫细说C#不是“栈类型”的值类型从生命周期聊存储位置》的最后匹夫以总结和后记的方式涉及到一部分迭代器的知识。但是觉得还是不够过瘾很多需要说清楚的内容还是含糊不清所以这周就专门写一下c#中的迭代器吧。回到目录0x01 你好迭代器首先思考一下在什么情景下我们需要使用到迭代器假设我们有一个数据容器可能是ArrayListTree等等对我们这些使用者来说我们显然希望这个数据容器能提供一种无需了解它的内部实现就可以获取其元素的方法无论它是Array还是List或者别的什么我们希望可以通过相同的方法达到我们的目的。此时迭代器模式iterator pattern便应运而生它通过持有迭代状态追踪当前元素并且识别下一个需要被迭代的元素从而可以让使用者透过特定的界面巡访容器中的每一个元素而不用了解底层的实现。那么在c#中迭代器到底是以一个怎样的面目出现的呢如我们所知它们被封装在IEnumerable和IEnumerator这两个接口中当然还有它们的泛型形式要注意的是泛型形式显然是强类型的。且IEnumeratorT实现了IDisposable接口。IEnumerable非泛型形式://IEnumerable非泛型形式[ComVisibleAttribute(True)] [GuidAttribute(496B0ABE-CDEE-11d3-88E8-00902754C43A)] public interface IEnumerable {IEnumerator GetEnumerator(); }IEnumerator非泛型形式://IEnumerator非泛型形式 [ComVisibleAttribute(true)] [GuidAttribute(496B0ABF-CDEE-11d3-88E8-00902754C43A)] public interface IEnumerator {Object Current {get;}bool MoveNext();void Reset(); }IEnumerable泛型形式://IEnumerable泛型形式public interface IEnumerableout T : IEnumerable {IEnumeratorT GetEnumerator();IEnumerator GetEnumerator(); }IEnumerator泛型形式://IEnumerator泛型形式public interface IEnumeratorout T : IDisposable, IEnumerator {void Dispose(); Object Current {get;} T Current {get;}bool MoveNext(); void Reset(); }[ComVisibleAttribute(true)] public interface IDisposable {void Dispose(); }IEnumerable接口定义了一个可以获取IEnumerator的方法——GetEnumerator()。而IEnumerator则在目标序列上实现循环迭代使用MoveNext()方法以及Current属性来实现直到你不再需要任何数据或者没有数据可以被返回。使用这个接口可以保证我们能够实现常见的foreach循环。为什么会有2个接口到此各位看官是否和曾经的匹夫有相同的疑惑呢那就是为何IEnumerable自己不直接实现MoveNext()方法、提供Current属性呢为何还需要额外的一个接口IEnumerator来专门做这个工作OK假设有两个不同的迭代器要对同一个序列进行迭代。当然这种情况很常见比如我们使用两个嵌套的foreach语句。我们自然希望两者相安无事不要互相影响彼此。所以自然而然的我们需要保证这两个独立的迭代状态能够被正确的保存、处理。这也正是IEnumerator要做的工作。而为了不违背单一职责原则不使IEnumerable拥有过多职责从而陷入分工不明的窘境所以IEnumerable自己并没有实现MoveNext()方法。迭代器的执行步骤为了更直观的了解一个迭代器匹夫这里提供一个小例子。using System; using System.Collections.Generic;class Class1 { static void Main(){foreach (string s in GetEnumerableTest()){Console.WriteLine(s);}}static IEnumerablestring GetEnumerableTest(){yield return begin;for (int i0; i 10; i){yield return i.ToString();}yield return end;} }输出结果如图OK那么匹夫就给各位捋一下这段代码的执行过程。Main调用GetEnumerableTest()方法GetEnumerableTest()方法会为我们创建一个编译器生成的新的类Class1/GetEnumerableTestc__Iterator0本例中的实例。注意此时GetEnumerableTest()方法中我们自己的代码尚未执行Main调用MoveNext()方法迭代器开始执行直到它遇到第一个yield return语句。此时迭代器会获取当前的值是“start”并且返回true以告知此时还有数据Main使用Current属性以获取数据并打印出来Main再次调用MoveNext()方法迭代器继续从上次遇到yield return的地方开始执行并且和之前一样直到遇到下一个yield return迭代器按照这种方式循环直到MoveNext()方法返回false以告知此时已经没有数据了这个例子中迭代器的执行过程匹夫已经给各位看官简单的描述了一下。但是还有几点需要关注的匹夫也想提醒各位注意一下。在第一次调用MoveNext()方法之前我们自己在GetEnumerableTest中的代码不会执行之后调用MoveNext()方法时会从上次暂停yield return的地方开始。编译器会保证GetEnumerableTest方法中的局部变量能够被保留换句话说虽然本例中的i是值类型实例但是它的值其实是被迭代器保存在堆上的这样才能保证每次调用MoveNext时它是可用的。这也是匹夫上一篇文章中说迭代器块中的局部变量会被分配在堆上的原因。好啦简单总结了一下C#中的迭代器的外观。那么接下来我们继续向内部前进来看看迭代器究竟是如何实现的。回到目录0x02 原来是状态机呀上一节我们已经从外部看到了IEnumerable和IEnumerator这两个接口的用法了但是它们的内部到底是如何实现的呢两者之间又有何区别呢既然要深入迭代器的内部这就是一个不得不面对的问题。那么匹夫就写一个小程序之后再通过反编译的方式看看在我们自己手动写的代码背后编译器究竟又给我们做了哪些工作吧。为了简便起见这个小程序仅仅实现一个按顺序返回0-9这10个数字的功能。IEnumerator的内部实现首先我们定义一个返回IEnumeratorT的方法TestIterator()。//IEnumeratorT测试using System; using System.Collections;class Test {static IEnumeratorint TestIterator(){for (int i 0; i 10; i){yield return i;}} }接下来我们看看反编译之后的代码探查一下编译器到底为我们做了什么吧。internal class Test {// Methods 注此时还没有执行任何我们写的代码private static IEnumeratorint TestIterator(){return new TestIteratord__0(0);}// Nested Types 编译器生成的类用来实现迭代器。 [CompilerGenerated]private sealed class TestIteratord__0 : IEnumeratorint, IEnumerator, IDisposable{// Fields 字段state和current是默认出现的private int 1__state;private int 2__current;public int i5__1;//i5__1来自我们迭代器块中的局部变量匹夫上一篇文章中提到过// Methods 构造函数初始化状态 [DebuggerHidden]public TestIteratord__0(int 1__state){this.1__state 1__state;}// 几乎所有的逻辑在这里private bool MoveNext(){switch (this.1__state){case 0:this.1__state -1;this.i5__1 0;while (this.i5__1 10){this.2__current this.i5__1;this.1__state 1;return true;Label_0046:this.1__state -1;this.i5__1;}break;case 1:goto Label_0046;}return false;}[DebuggerHidden]void IEnumerator.Reset(){throw new NotSupportedException();}void IDisposable.Dispose(){}// Propertiesint IEnumeratorint.Current{[DebuggerHidden]get{return this.2__current;}}object IEnumerator.Current{[DebuggerHidden]get{return this.2__current;}}} }我们先全面的看一下反编译之后的代码可以发现几乎所有的逻辑都发生在MoveNext()方法中。那么之后我们再详细介绍下它现在我们先从上到下把代码捋一遍。这段代码给人的第一印象就是命名似乎很不雅观。的确这种在正常的C#代码中不会出现的命名在编译器生成的代码中却是常常出现。因为这样就可以避免和已经存在的正常名字发生冲突的可能性。调用TestIterator()方法的结果仅仅是调用了TestIteratord__0编译器生成的用来实现迭代器的类的构造函数。而这个构造函数会设置迭代器的初始状态此时的参数为0而构造函数会将0赋值给记录迭代器状态的字段 this.1__state 1__state;。注意此时我们自己的代码并没有执行。TestIteratord__0这个类实现了3个接口IEnumeratorint, IEnumerator, IDisposable。IDisposable的实现十分重要。因为foreach语句会在它自己的finally代码块中调用实现了IDisposable接口的迭代器的Dispose方法。TestIteratord__0类有3个字段1__state2__current i5__1。其中1__state私有字段标识迭代器的状态2__current私有字段则追踪当前的值而i5__1共有字段则是我们在迭代器块中定义的局部变量i。MoveNext()方法的实现则依托与switch语句。根据状态机的状态执行不同的代码。在本例中Dispose方法什么都没有做。在IEnumerator和IEnumeratorint的实现中Current都是单纯的返回2__current的值。OKIEnumerator接口我们看完了。下面再来看看另一个接口IEnumerable吧。IEnumerator VS IEnumerable依样画葫芦这次我们仍然是写一个实现按顺序返回0-9这10个数字的功能的小程序只不过返回类型变为IEnumerableT。using System; using System.Collections.Generic;class Test {static IEnumerableint TestIterator(){for (int i 0; i 10; i){yield return i;}} }之后我们同样通过反编译看看编译器又背着我们做了什么。internal class Test {private static IEnumerableint TestIterator(){return new TestIteratord__0(-2);}private sealed class TestIteratord__0 : IEnumerableint, IEnumerable, IEnumeratorint, IEnumerator, IDisposable{// Fieldsprivate int 1__state;private int 2__current;private int l__initialThreadId;public int count5__1;public TestIteratord__0(int 1__state){this.1__state 1__state;this.l__initialThreadId Thread.CurrentThread.ManagedThreadId;}private bool MoveNext(){switch (this.1__state){case 0:this.1__state -1;this.count5__1 0;while (this.count5__1 10){this.2__current this.count5__1;this.1__state 1;return true;Label_0046:this.1__state -1;this.count5__1;}break;case 1:goto Label_0046;}return false;}IEnumeratorint IEnumerableint.GetEnumerator(){if ((Thread.CurrentThread.ManagedThreadId this.l__initialThreadId) (this.1__state -2)){this.1__state 0;return this;}return new Test.TestIteratord__0(0);}IEnumerator IEnumerable.GetEnumerator(){return ((IEnumerableInt32) this).GetEnumerator();}void IEnumerator.Reset(){throw new NotSupportedException();}void IDisposable.Dispose(){}int IEnumeratorint.Current{get{return this.2__current;}}object IEnumerator.Current{get{return this.2__current;}}} }看到反编译出的代码我们就很容易能对比出区别。TestIteratord__0类不仅实现了IEnumerableint 接口而且还实现了IEnumeratorint接口。IEnumerator和IEnumeratorint的实现都和上面一样。IEnumerator的Reset方法会抛出NotSupportedException异常而IEnumerator和IEnumeratorint的Current仍旧会返回2__current字段的值。TestIterator()方法调用TestIteratord__0类的构造函数时传入的参数由上面的0变成了-2“new TestIteratord__0(-2);”。也就是说此时的初始状态是-2。又多了一个新的私有字段“l__initialThreadId”且会在TestIteratord__0的构造函数中被赋值用来标识创建该实例的线程。实现IEnumerable的GetEnumerator方法在GetEnumerator方法中要么将状态置为0并返回thisthis.1__state 0return this要么就返回一个新的TestIteratord__0实例且初始状态置为0return new Test.TestIteratord__0(0);所以从这些对比中我们能发现些什么吗思考一下我们经常使用的一些用法包括匹夫在上一节中提供的小例子。不错我们会创建一个IEnumerableT的实例之后一些语句例如foreach会去调用GetEnumerator方法获取一个EnumeratorT的实例之后迭代数据最终结束后释放掉迭代器的实例这一步foreach会帮我们做。而最初我们得到的IEnumerableT实例在第一次调用GetEnumerator方法获得了一个EnumeratorT实例之后就再没有用到了。而分析IEnumerable的GetEnumerator方法 IEnumeratorint IEnumerableint.GetEnumerator(){if ((Thread.CurrentThread.ManagedThreadId this.l__initialThreadId) (this.1__state -2)){this.1__state 0;return this;}return new Test.TestIteratord__0(0);}我们可以发现-2这个状态也就是此时的初始状态表明了GetEnumerator()方法还没有执行。而0这个状态则表明已经准备好了迭代但是MoveNext()尚未调用过。当在不同的线程上调用GetEnumerator方法或者是状态不是-2证明已经不是初始状态了则GetEnumerator方法会返回一个TestIteratord__0类的新实例用来保存不同的状态。回到目录0x03 状态管理OK我们深入了迭代器的内部发现了原来它的实现主要依靠的是一个状态机。那么下面就让匹夫继续和大伙聊聊这个状态机是如何管理状态的。状态切换根据Ecma-334标准也就是c#语言标准的第26.2 Enumerator objects小节我们可以知道迭代器有4种可能状态before状态running状态suspended状态after状态而其中before状态是作为初始状态出现的。在我们讨论状态如何切换之前匹夫还要带领大家回想一下上面提到的也就是在调用一个使用了迭代器块返回类型为一个IEnumerator或IEnumerable接口的方法时这个方法并非立刻执行我们自己写的代码的。而是会创建一个编译器生成的类的实例之后当调用MoveNext()方法时当然如果方法的返回类型是IEnumerable则要先调用GetEnumerator()方法我们的代码才会开始执行直到遇到第一个yield return语句或yield break语句此时会返回一个布尔值来判断迭代是否结束。当下次再调用MoveNext()方法时我们的方法会继续从上一个yield return语句处开始执行。为了能够直观的观察状态的切换下面小匹夫提供一个类似于《深入理解C#》这本书中的例子class Test {static IEnumerableint TestStateChange(){Console.WriteLine(----我TestStateChange是第一行代码);Console.WriteLine(----我是第一个yield return前的代码);yield return 1;Console.WriteLine(----我是第一个yield return后的代码);Console.WriteLine(----我是第二个yield return前的代码);yield return 2;Console.WriteLine(----我是第二个yield return前的代码);}static void Main(){Console.WriteLine(调用TestStateChange);IEnumerableint iteratorable TestStateChange();Console.WriteLine(调用GetEnumerator);IEnumeratorint iterator iteratorable.GetEnumerator();Console.WriteLine(调用MoveNext());bool hasNext iterator.MoveNext();Console.WriteLine(是否有数据{0}; Current{1}, hasNext, iterator.Current);Console.WriteLine(第二次调用MoveNext);hasNext iterator.MoveNext();Console.WriteLine(是否还有数据{0}; Current{1}, hasNext, iterator.Current);Console.WriteLine(第三次调用MoveNext);hasNext iterator.MoveNext();Console.WriteLine(是否还有数据{0}, hasNext);} }之后我们运行这段代码看看结果如何。可见代码的执行顺序就是匹夫刚刚总结的那样。那么我们将这段编译后的代码再反编译回C#看看编译器到底是如何处理这里的状态切换的。这里我们只关心两个方法首先是GetEnumerator方法。其次是MoveNext方法。[DebuggerHidden] IEnumeratorint IEnumerableint.GetEnumerator() {if ((Environment.CurrentManagedThreadId this.l__initialThreadId) (this.1__state -2)){this.1__state 0;return this;}return new Test.TestStateChanged__0(0); }看GetEnumerator方法我们可以发现此时的初始状态是-2。不过一旦调用GetEnumerator则会将状态置为0。也就是状态从最初的-2,在调用过GetEnumerator方法后变成了0。我们再来看看MoveNext方法。private bool MoveNext() {switch (this.1__state){case 0:this.1__state -1;Console.WriteLine(----我TestStateChange是第一行代码);Console.WriteLine(----我是第一个yield return前的代码);this.2__current 1;this.1__state 1;return true;case 1:this.1__state -1;Console.WriteLine(----我是第一个yield return后的代码);Console.WriteLine(----我是第二个yield return前的代码);this.2__current 2;this.1__state 2;return true;case 2:this.1__state -1;Console.WriteLine(----我是第二个yield return前的代码);break;}return false; }由于第一次调用MoveNext方法发生在调用GetEnumerator方法之后所以此时状态已经变成了0。可以清晰的看到此时从0——1——2——-1这样的状态切换过程。而且还要注意每个分支中this.1__state都会首先被置为-1this.1__state -1。之后才会根据不同的阶段赋值不同的值。而这些不同的值也就用来标识代码从哪里恢复执行。我们再拿之前实现了按顺序返回0-9这10个数字的小程序的状态管理作为例子来让我们更加深刻的理解迭代器除了刚刚的例子还有什么手段可以用来实现“当下次再调用MoveNext()方法时我们的方法会继续从上一个yield return语句处开始执行。”这一个功能的。private bool MoveNext(){switch (this.1__state){case 0:this.1__state -1;this.i5__1 0;while (this.i5__1 10){this.2__current this.i5__1;this.1__state 1;return true;Label_0046:this.1__state -1;this.i5__1;}break;case 1:goto Label_0046;}return false;}如代码中黄色色带标出的语句不错此时状态机是靠着goto语句实现半路插入进而实现了从yield return处继续执行的功能。好吧让我们总结一下关于迭代器内部状态机的状态切换-2状态只有IEnumerable才有表明在第一次调用GetEnumerator之前的状态。-1状态即上文中提到的C#语言标准中规定的Running状态表明此时迭代器正在执行。当然也会用于After状态例如上例中的case 2中this.1__state被赋值为-1但是此时迭代结束了。0状态即上文中提到的Before状态表明MoveNext()还一次都没有调用过。正数123...主要用来标识从遇到yield之后代码从哪里恢复执行。回到目录0x04 总结通过匹夫上文的分析可以看出迭代器的实现的确十分复杂。不过值得庆幸的是很多工作都由编译器在幕后为我们做好了。那么本文就到此结束。欢迎大家探讨。本作品采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可我的博客欢迎复制共享但在同时希望保留我的署名权陈嘉栋慕容小匹夫并且不得用于商业用途。如您有任何疑问或者授权方面的协商请给我留言。来源 http://www.cnblogs.com/murongxiaopifu/p/4437432.html 来自为知笔记(Wiz)转载于:https://www.cnblogs.com/zhiGamer/p/6230542.html
http://www.yutouwan.com/news/486034/

相关文章:

  • 美业网站建设重庆一般建一个网站需要多少钱
  • 网站制作培训班织梦网站首页怎么修改
  • 西二旗网站建设天长网站制作
  • 鞋网站模版企业官网推广
  • 网上做兼职网站正规搜索引擎排名谷歌
  • 静态网站怎么建设wordpress进入文字版
  • cms适合做什么网站网站建设 后端前端
  • 卖钢材做哪个宣传网站如果提高网站
  • 营销型网站首页模板网页设计师培训排行榜
  • 四川省广安建设局网站久久网站建设
  • 手机网站制作 尺寸成都建设路小学网站
  • 同域名网站改版需要把之前网站里的文章都拷贝过来吗?创建小型网站的步骤
  • 宁波网站建设h5工业互联网平台有哪些
  • 成都快速做网站公司品牌宣传方案
  • 沂源网站开发利用淘宝视频服务做视频网站
  • vue 实现网站开发如何建网站赚取佣金
  • 泉州网站建设公司招聘销售合肥软件建设
  • 建立什么本地网站赚钱嵌入式软件开发培训机构
  • wordpress添加背景音乐南京seo网站优化
  • wordpress 产品网站网站关键词排名全掉了
  • 用vs做网站界面网站站内推广怎么做
  • 水印在线制作网站华能集团网站建设方案项目分析
  • 商城网站如何做建网站需要什么服务器
  • 装饰公司看的设计网站旅游电子商务网站建设规划
  • 建设网站的建议中国互联网设计公司
  • 怎样让百度收录网站新网页游戏开服表
  • 静态网页做的网站怎么发到网上古楼角网站建设
  • 娱乐网站建设公司南宁网页搭建软件
  • 福田皇岗社区做网站做网站花都区
  • 个人网站备案流程短视频营销方式