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

做淘宝是不是要两根网站搜狐财经峰会

做淘宝是不是要两根网站,搜狐财经峰会,政府部门建立网站的作用,怎么做淘宝客导购网站推广前言 当我们编写 C# 代码时,经常需要处理大量的数据集合。在传统的方式中,我们往往需要先将整个数据集合加载到内存中,然后再进行操作。但是如果数据集合非常大,这种方式就会导致内存占用过高,甚至可能导致程序崩溃。 …

前言

    当我们编写 C# 代码时,经常需要处理大量的数据集合。在传统的方式中,我们往往需要先将整个数据集合加载到内存中,然后再进行操作。但是如果数据集合非常大,这种方式就会导致内存占用过高,甚至可能导致程序崩溃。

    C# 中的yield return机制可以帮助我们解决这个问题。通过使用yield return,我们可以将数据集合按需生成,而不是一次性生成整个数据集合。这样可以大大减少内存占用,并且提高程序的性能。

    在本文中,我们将深入讨论 C# 中yield return的机制和用法,帮助您更好地理解这个强大的功能,并在实际开发中灵活使用它。

使用方式

上面我们提到了yield return将数据集合按需生成,而不是一次性生成整个数据集合。接下来通过一个简单的示例,我们看一下它的工作方式是什么样的,以便加深对它的理解

foreach (var num in GetInts())
{Console.WriteLine("外部遍历了:{0}", num);
}IEnumerable<int> GetInts()
{for (int i = 0; i < 5; i++){Console.WriteLine("内部遍历了:{0}", i);yield return i;}
}

首先,在GetInts方法中,我们使用yield return关键字来定义一个迭代器。这个迭代器可以按需生成整数序列。在每次循环时,使用yield return返回当前的整数。通过1foreach循环来遍历 GetInts方法返回的整数序列。在迭代时GetInts方法会被执行,但是不会将整个序列加载到内存中。而是在需要时,按需生成序列中的每个元素。在每次迭代时,会输出当前迭代的整数对应的信息。所以输出的结果为

内部遍历了:0
外部遍历了:0
内部遍历了:1
外部遍历了:1
内部遍历了:2
外部遍历了:2
内部遍历了:3
外部遍历了:3
内部遍历了:4
外部遍历了:4

可以看到,整数序列是按需生成的,并且在每次生成时都会输出相应的信息。这种方式可以大大减少内存占用,并且提高程序的性能。当然从c# 8开始异步迭代的方式同样支持

await foreach (var num in GetIntsAsync())
{Console.WriteLine("外部遍历了:{0}", num);
}async IAsyncEnumerable<int> GetIntsAsync()
{for (int i = 0; i < 5; i++){await Task.Yield();Console.WriteLine("内部遍历了:{0}", i);yield return i;}
}

和上面不同的是,如果需要用异步的方式,我们需要返回IAsyncEnumerable类型,这种方式的执行结果和上面同步的方式执行的结果是一致的,我们就不做展示了。上面我们的示例都是基于循环持续迭代的,其实使用yield return的方式还可以按需的方式去输出,这种方式适合灵活迭代的方式。如下示例所示

foreach (var num in GetInts())
{Console.WriteLine("外部遍历了:{0}", num);
}IEnumerable<int> GetInts()
{Console.WriteLine("内部遍历了:0");yield return 0;Console.WriteLine("内部遍历了:1");yield return 1;Console.WriteLine("内部遍历了:2");yield return 2;
}

foreach循环每次会调用GetInts()方法,GetInts()方法的内部便使用yield return关键字返回一个结果。每次遍历都会去执行下一个yield return。所以上面代码输出的结果是

内部遍历了:0
外部遍历了:0
内部遍历了:1
外部遍历了:1
内部遍历了:2
外部遍历了:2

探究本质

上面我们展示了yield return如何使用的示例,它是一种延迟加载的机制,它可以让我们逐个地处理数据,而不是一次性地将所有数据读取到内存中。接下来我们就来探究一下神奇操作的背后到底是如何实现的,方便让大家更清晰的了解迭代体系相关。

foreach本质

首先我们来看一下foreach为什么可以遍历,也就是如果可以被foreach遍历的对象,被遍历的操作需要满足哪些条件,这个时候我们可以反编译工具来看一下编译后的代码是什么样子的,相信大家最熟悉的就是List<T>集合的遍历方式了,那我们就用List<T>的示例来演示一下

List<int> ints = new List<int>();
foreach(int item in ints)
{Console.WriteLine(item);
}

上面的这段代码很简单,我们也没有给它任何初始化的数据,这样可以排除干扰,让我们能更清晰的看到反编译的结果,排除其他干扰。它反编译后的代码是这样的

List<int> list = new List<int>();
List<int>.Enumerator enumerator = list.GetEnumerator();
try
{while (enumerator.MoveNext()){int current = enumerator.Current;Console.WriteLine(current);}
}
finally
{((IDisposable)enumerator).Dispose();
}

可以反编译代码的工具有很多,我用的比较多的一般是ILSpydnSpydotPeek和在线c#反编译网站sharplab.io,其中dnSpy还可以调试反编译的代码。

通过上面的反编译之后的代码我们可以看到foreach会变编译成一个固定的结构

Enumerator enumerator = list.GetEnumerator();
while (enumerator.MoveNext())
{var current = enumerator.Current;
}

通过这段固定的结构我们总结一下foreach的工作原理

  • 可以被foreach的对象需要要包含GetEnumerator()方法
  • 迭代器对象包含MoveNext()方法和Current属性
  • MoveNext()方法返回bool类型,判断是否可以继续迭代。Current属性返回当前的迭代结果。

我们可以看一下List<T>类可迭代的源码结构是如何实现的

public class List<T> : IList<T>, IList, IReadOnlyList<T>
{public Enumerator GetEnumerator() => new Enumerator(this);IEnumerator<T> IEnumerable<T>.GetEnumerator() => Count == 0 ? SZGenericArrayEnumerator<T>.Empty : GetEnumerator();IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<T>)this).GetEnumerator();public struct Enumerator : IEnumerator<T>, IEnumerator{public T Current => _current!;public bool MoveNext(){}}
}

这里涉及到了两个核心的接口IEnumerable<IEnumerator,他们两个定义了可以实现迭代的能力抽象,实现方式如下

public interface IEnumerable
{IEnumerator GetEnumerator();
}public interface IEnumerator
{bool MoveNext();object Current{ get; }void Reset();
}

如果类实现IEnumerable接口并实现了GetEnumerator()方法便可以被foreach,迭代的对象是IEnumerator类型,包含可MoveNext()方法和Current属性。上面的接口是原始对象的方式,这种操作都是针对object类型集合对象。我们实际开发过程中大多数都是使用的泛型集合,当然也有对应的实现方式,如下所示

public interface IEnumerable<out T> : IEnumerable
{new IEnumerator<T> GetEnumerator();
}public interface IEnumerator<out T> : IDisposable, IEnumerator
{new T Current{ get; }
}

可以被foreach迭代并不意味着一定要去实现IEnumerable接口,这只是给我们提供了一个可以被迭代的抽象的能力。只要类中包含GetEnumerator()方法并返回一个迭代器,迭代器里包含返回bool类型的MoveNext()方法和获取当前迭代对象的Current属性即可。

yield return本质

上面我们看到了可以被foreach迭代的本质是什么,那么yield return的返回值可以被IEnumerable<T>接收说明其中必有蹊跷,我们反编译一下我们上面的示例看一下反编译之后代码,为了方便大家对比反编译结果,这里我把上面的示例再次粘贴一下

foreach (var num in GetInts())
{Console.WriteLine("外部遍历了:{0}", num);
}IEnumerable<int> GetInts()
{for (int i = 0; i < 5; i++){Console.WriteLine("内部遍历了:{0}", i);yield return i;}
}

它的反编译结果,这里咱们就不全部展示了,只展示一下核心的逻辑

//foeach编译后的结果
IEnumerator<int> enumerator = GetInts().GetEnumerator();
try
{while (enumerator.MoveNext()){int current = enumerator.Current;Console.WriteLine("外部遍历了:{0}", current);}
}
finally
{if (enumerator != null){enumerator.Dispose();}
}//GetInts方法编译后的结果
private IEnumerable<int> GetInts()
{<GetInts>d__1 <GetInts>d__ = new <GetInts>d__1(-2);<GetInts>d__.<>4__this = this;return <GetInts>d__;
}

这里我们可以看到GetInts()方法里原来的代码不见了,而是多了一个<GetInts>d__1 l类型,也就是说yield return本质是语法糖。我们看一下<GetInts>d__1类的实现

//生成的类即实现了IEnumerable接口也实现了IEnumerator接口
//说明它既包含了GetEnumerator()方法,也包含MoveNext()方法和Current属性
private sealed class <>GetIntsd__1 : IEnumerable<int>, IEnumerable, IEnumerator<int>, IEnumerator, IDisposable
{private int <>1__state;//当前迭代结果private int <>2__current;private int <>l__initialThreadId;public C <>4__this;private int <i>5__1;//当前迭代到的结果int IEnumerator<int>.Current{get{ return <>2__current; }}//当前迭代到的结果object IEnumerator.Current{get{ return <>2__current; }}//构造函数包含状态字段,变向说明靠状态机去实现核心流程流转public <GetInts>d__1(int <>1__state){this.<>1__state = <>1__state;<>l__initialThreadId = Environment.CurrentManagedThreadId;}//核心方法MoveNextprivate bool MoveNext(){int num = <>1__state;if (num != 0){if (num != 1){return false;}//控制状态<>1__state = -1;//自增 也就是代码里循环的i++<i>5__1++;}else{<>1__state = -1;<i>5__1 = 0;}//循环终止条件 上面循环里的i<5if (<i>5__1 < 5){Console.WriteLine("内部遍历了:{0}", <i>5__1);//把当前迭代结果赋值给Current属性<>2__current = <i>5__1;<>1__state = 1;//说明可以继续迭代return true;}//迭代结束return false;}//IEnumerator的MoveNext方法bool IEnumerator.MoveNext(){return this.MoveNext();}//IEnumerable的IEnumerable方法IEnumerator<int> IEnumerable<int>.IEnumerable(){//实例化<GetInts>d__1实例<GetInts>d__1 <GetInts>d__;if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId){<>1__state = 0;<GetInts>d__ = this;}else{//给状态机初始化<GetInts>d__ = new <GetInts>d__1(0);<GetInts>d__.<>4__this = <>4__this;}//因为<GetInts>d__1实现了IEnumerator接口所以可以直接返回return <GetInts>d__;}IEnumerator IEnumerable.GetEnumerator(){//因为<GetInts>d__1实现了IEnumerator接口所以可以直接转换return ((IEnumerable<int>)this).GetEnumerator();}void IEnumerator.Reset(){}void IDisposable.Dispose(){}
}

通过它生成的类我们可以看到,该类即实现了IEnumerable接口也实现了IEnumerator接口说明它既包含了GetEnumerator()方法,也包含MoveNext()方法和Current属性。用这一个类就可以满足可被foeach别带的核心结构。我们手动写的for代码被包含到了MoveNext()方法里,它包含了定义的状态机制代码,并且根据当前的状态机代码将迭代移动到下一个元素。我们大概讲解一下我们的for代码被翻译到MoveNext()方法里的执行流程

  • 首次迭代时<>1__state被初始化成0,代表首个被迭代的元素,这个时候Current初始值为0,循环控制变量<i>5__1初始值也为0。
  • 判断是否满足终止条件,不满足则执行循环里的逻辑。并更改装填机<>1__state为1,代表首次迭代执行完成。
  • 循环控制变量<i>5__1继续自增并更改并更改装填机<>1__state为-1,代表可持续迭代。并循环执行循环体的自定义逻辑。
  • 不满足迭代条件则返回false,也就是代表了MoveNext()以不满足迭代条件while (enumerator.MoveNext())逻辑终止。

上面我们还展示了另一种yield return的方式,就是同一个方法里包含多个yield return的形式

IEnumerable<int> GetInts()
{Console.WriteLine("内部遍历了:0");yield return 0;Console.WriteLine("内部遍历了:1");yield return 1;Console.WriteLine("内部遍历了:2");yield return 2;
}

上面这段代码反编译的结果如下所示,这里咱们只展示核心的方法MoveNext()的实现

private bool MoveNext()
{switch (<>1__state){default:return false;case 0:<>1__state = -1;Console.WriteLine("内部遍历了:0");<>2__current = 0;<>1__state = 1;return true;case 1:<>1__state = -1;Console.WriteLine("内部遍历了:1");<>2__current = 1;<>1__state = 2;return true;case 2:<>1__state = -1;Console.WriteLine("内部遍历了:2");<>2__current = 2;<>1__state = 3;return true;case 3:<>1__state = -1;return false;}
}

通过编译后的代码我们可以看到,多个yield return的形式会被编译成switch...case的形式,有几个yield return则会编译成n+1case,多出来的一个case则代表的MoveNext()终止条件,也就是返回false的条件。其它的case则返回true表示可以继续迭代。

IAsyncEnumerable接口

上面我们展示了同步yield return方式,c# 8开始新增了IAsyncEnumerable<T>接口,用于完成异步迭代,也就是迭代器逻辑里包含异步逻辑的场景。IAsyncEnumerable<T>接口的实现代码如下所示

public interface IAsyncEnumerable<out T>
{IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default);
}public interface IAsyncEnumerator<out T> : IAsyncDisposable
{ValueTask<bool> MoveNextAsync();T Current { get; }
}

它最大的不同则是同步的IEnumerator包含的是MoveNext()方法返回的是boolIAsyncEnumerator接口包含的是MoveNextAsync()异步方法,返回的是ValueTask<bool>类型。所以上面的示例代码

await foreach (var num in GetIntsAsync())
{Console.WriteLine("外部遍历了:{0}", num);
}

所以这里的await虽然是加在foreach上面,但是实际作用的则是每一次迭代执行的MoveNextAsync()方法。可以大致理解为下面的工作方式

IAsyncEnumerator<int> enumerator = list.GetAsyncEnumerator();
while (enumerator.MoveNextAsync().GetAwaiter().GetResult())
{var current = enumerator.Current;
}

当然,实际编译成的代码并不是这个样子的,我们在之前的文章<研究c#异步操作async await状态机的总结>一文中讲解过async await会被编译成IAsyncStateMachine异步状态机,所以IAsyncEnumerator<T>结合yield return的实现比同步的方式更加复杂而且包含更多的代码,不过实现原理可以结合同步的方式类比一下,但是要同时了解异步状态机的实现,这里咱们就不过多展示异步yield return的编译后实现了,有兴趣的同学可以自行了解一下。

foreach增强

c# 9增加了对foreach的增强的功能,即通过扩展方法的形式,对原本具备包含foreach能力的对象增加GetEnumerator()方法,使得普通类在不具备foreach的能力的情况下也可以使用来迭代。它的使用方式如下

Foo foo = new Foo();
foreach (int item in foo)
{Console.WriteLine(item);
}public class Foo
{public List<int> Ints { get; set; } = new List<int>();
}public static class Bar
{//给Foo定义扩展方法public static IEnumerator<int> GetEnumerator(this Foo foo){foreach (int item in foo.Ints){yield return item;}}
}

这个功能确实比较强大,满足开放封闭原则,我们可以在不修改原始代码的情况,增强代码的功能,可以说是非常的实用。我们来看一下它的编译后的结果是啥

Foo foo = new Foo();
IEnumerator<int> enumerator = Bar.GetEnumerator(foo);
try
{while (enumerator.MoveNext()){int current = enumerator.Current;Console.WriteLine(current);}
}
finally
{if (enumerator != null){enumerator.Dispose();}
}

这里我们看到扩展方法GetEnumerator()本质也是语法糖,会把扩展能力编译成扩展类.GetEnumerator(被扩展实例)的方式。也就是我们写代码时候的原始方式,只是编译器帮我们生成了它的调用方式。接下来我们看一下GetEnumerator()扩展方法编译成了什么

public static IEnumerator<int> GetEnumerator(Foo foo)
{<GetEnumerator>d__0 <GetEnumerator>d__ = new <GetEnumerator>d__0(0);<GetEnumerator>d__.foo = foo;return <GetEnumerator>d__;
}

看到这个代码是不是觉得很眼熟了,不错和上面yield return本质这一节里讲到的语法糖生成方式是一样的了,同样的编译时候也是生成了一个对应类,这里的类是<GetEnumerator>d__0,我们看一下该类的结构

private sealed class <GetEnumerator>d__0 : IEnumerator<int>, IEnumerator, IDisposable
{private int <>1__state;private int <>2__current;public Foo foo;private List<int>.Enumerator <>s__1;private int <item>5__2;int IEnumerator<int>.Current{get{ return <>2__current; }}object IEnumerator.Current{get{ return <>2__current; }}public <GetEnumerator>d__0(int <>1__state){this.<>1__state = <>1__state;}private bool MoveNext(){try{int num = <>1__state;if (num != 0){if (num != 1){return false;}<>1__state = -3;}else{<>1__state = -1;//因为示例中的Ints我们使用的是List<T><>s__1 = foo.Ints.GetEnumerator();<>1__state = -3;}//因为上面的扩展方法里使用的是foreach遍历方式//这里也被编译成了实际生产方式if (<>s__1.MoveNext()){<item>5__2 = <>s__1.Current;<>2__current = <item>5__2;<>1__state = 1;return true;}<>m__Finally1();<>s__1 = default(List<int>.Enumerator);return false;}catch{((IDisposable)this).Dispose();throw;}}bool IEnumerator.MoveNext(){return this.MoveNext();}void IDisposable.Dispose(){}void IEnumerator.Reset(){}private void <>m__Finally1(){}
}

看到编译器生成的代码,我们可以看到yield return生成的代码结构都是一样的,只是MoveNext()里的逻辑取决于我们写代码时候的具体逻辑,不同的逻辑生成不同的代码。这里咱们就不在讲解它生成的代码了,因为和上面咱们讲解的代码逻辑是差不多的。

总结

    通过本文我们介绍了c#中的yield return语法,并探讨了由它带来的一些思考。我们通过一些简单的例子,展示了yield return的使用方式,知道了迭代器来是如何按需处理大量数据。同时,我们通过分析foreach迭代和yield return语法的本质,讲解了它们的实现原理和底层机制。好在涉及到的知识整体比较简单,仔细阅读相关实现代码的话相信会了解背后的实现原理,这里就不过多赘述了。

    当你遇到挑战和困难时,请不要轻易放弃。无论你面对的是什么,只要你肯努力去尝试,去探索,去追求,你一定能够克服困难,走向成功。记住,成功不是一蹴而就的,它需要我们不断努力和坚持。相信自己,相信自己的能力,相信自己的潜力,你一定能够成为更好的自己。


文章转载自:
http://gerry.mzpd.cn
http://ultraleftist.mzpd.cn
http://sozin.mzpd.cn
http://assegai.mzpd.cn
http://mikvah.mzpd.cn
http://launching.mzpd.cn
http://generativist.mzpd.cn
http://fortissimo.mzpd.cn
http://seesaw.mzpd.cn
http://parasitoid.mzpd.cn
http://nabber.mzpd.cn
http://heintzite.mzpd.cn
http://fireball.mzpd.cn
http://hazel.mzpd.cn
http://jonnock.mzpd.cn
http://philharmonic.mzpd.cn
http://default.mzpd.cn
http://rifty.mzpd.cn
http://backpaddle.mzpd.cn
http://oaken.mzpd.cn
http://thermoset.mzpd.cn
http://geminal.mzpd.cn
http://disk.mzpd.cn
http://shopper.mzpd.cn
http://mysterioso.mzpd.cn
http://davis.mzpd.cn
http://rivalrousness.mzpd.cn
http://rally.mzpd.cn
http://renaissance.mzpd.cn
http://spinstry.mzpd.cn
http://beaverboard.mzpd.cn
http://sprinkle.mzpd.cn
http://haplite.mzpd.cn
http://dnepropetrovsk.mzpd.cn
http://trustworthiness.mzpd.cn
http://volcanologist.mzpd.cn
http://antifreeze.mzpd.cn
http://tellurous.mzpd.cn
http://mentalism.mzpd.cn
http://security.mzpd.cn
http://polyantha.mzpd.cn
http://babi.mzpd.cn
http://phenomenize.mzpd.cn
http://fireless.mzpd.cn
http://washbowl.mzpd.cn
http://gratulate.mzpd.cn
http://novemdecillion.mzpd.cn
http://ingestible.mzpd.cn
http://fumatorium.mzpd.cn
http://lammy.mzpd.cn
http://ecad.mzpd.cn
http://behave.mzpd.cn
http://bona.mzpd.cn
http://kirovabad.mzpd.cn
http://phosphoryl.mzpd.cn
http://transitron.mzpd.cn
http://bipropellant.mzpd.cn
http://phonic.mzpd.cn
http://flatness.mzpd.cn
http://afrikander.mzpd.cn
http://befuddle.mzpd.cn
http://preemphasis.mzpd.cn
http://strandloper.mzpd.cn
http://repartimiento.mzpd.cn
http://petrifaction.mzpd.cn
http://anurous.mzpd.cn
http://adrenocorticotro.mzpd.cn
http://lysozyme.mzpd.cn
http://exarticulate.mzpd.cn
http://eskimo.mzpd.cn
http://pardner.mzpd.cn
http://agamy.mzpd.cn
http://haugh.mzpd.cn
http://bewail.mzpd.cn
http://northwestward.mzpd.cn
http://chimaeric.mzpd.cn
http://phthisical.mzpd.cn
http://reveler.mzpd.cn
http://inexhaustive.mzpd.cn
http://limina.mzpd.cn
http://cancan.mzpd.cn
http://yamoussoukro.mzpd.cn
http://embryulcia.mzpd.cn
http://darkroom.mzpd.cn
http://leisurely.mzpd.cn
http://haematocyte.mzpd.cn
http://carpometacarpus.mzpd.cn
http://opponens.mzpd.cn
http://midnoon.mzpd.cn
http://decalage.mzpd.cn
http://postsynchronization.mzpd.cn
http://cognizance.mzpd.cn
http://dietarian.mzpd.cn
http://coze.mzpd.cn
http://craiova.mzpd.cn
http://binder.mzpd.cn
http://covellite.mzpd.cn
http://philogyny.mzpd.cn
http://potable.mzpd.cn
http://gentamicin.mzpd.cn
http://www.15wanjia.com/news/75465.html

相关文章:

  • 关于网站优化的文章西安seo公司哪家好
  • 自己免费怎么制作网站江西seo推广软件
  • 北京活动策划公司排行优化措施最新回应
  • 设计相关网站磁力珠
  • 泾阳做网站做营销怎样才能吸引客户
  • phpstudy配置wordpress商丘关键词优化推广
  • 注册网站的好处简述网络营销的主要方法
  • 网站群如何做网站南宁做网站公司
  • 大气时尚的网站怎么引流怎么推广自己的产品
  • 网站建设合同 售后维护期适合40岁女人的培训班
  • 网站建设调查问卷seo 优化 服务
  • 网站主编 做啥进一步优化营商环境
  • 网站开发合作运营平台合同磁力屋 最好用
  • wordpress两栏响应式主题优化大师win7官方免费下载
  • 网站建设怎么做分录网络营销是指什么
  • 外国人搞笑做视频网站百度关键词怎么做排名
  • 云南网站建设哪家权威海南百度推广公司有哪些
  • 北京设计院排名100强长沙seo结算
  • 关于做膳食的一些网站抚顺网络推广
  • 上海高端模板建站网站是怎么做的
  • wordpress表单功能新手如何学seo
  • 网站服务器是干什么的网络优化公司有哪些
  • 电子商务有什么岗位杭州seo教程
  • 网站实现隶书繁体网络广告文案范文
  • 崇明网站建设建什么网站可以长期盈利
  • 云南网站建设方案上海哪家优化公司好
  • 怎样申请一个免费网站推广软文怎么写样板
  • 网站域名 空间申请表百度收录查询网址
  • 做婚介打么网站好徐州百度推广电话
  • 成人做暧视频观看网站市场推广方案范文