(转载)yield 语法糖

微软登录OAuth 2.0授权流程 OAuth 2.0是一个关于授权的开放网络标准,它允许第三方应用获取用户数据,是目前最流行的授权机制。微软身份平台支持OAuth 2.0授权码流程,使得客户端应用程序能够获得对受保护资源(如网络API)的授权访问。这一流程在保障用户数据安全的前提下,实现了应用的便捷登录与数据共享。当用户在一个支持OAuth 2.0的应用上,希望使用微软账号进行登录时,OAuth 2.0授权流程即被触发。 阅读详情

在定义API的时候,对于一些返回集合对象的方法,很多人喜欢将返回类型定义成IEnumerable<T>,这本没有什么问题。这里要说的是另一个问题:对于返回类型为IEnumerable<T>的方法来说,我们可以使用yield return的方式来输出返回集合的元素。但是如果我们不了解yield 关键字背后的实现机制,很有可能造成很大的问题。

这是一个WCF相关的问题,我想99%的人都有可能会犯这样的错误——即使你对yield了解得非常透彻。闲话少说,我们通过一个简单的实例来说明这个问题。我们定义了如下一个IDemoService接口作为服务契约,唯一的方法GetItems返回一个类型为IEnumerable<string>对象,并且具有唯一字符串参数category。

   1:  [ServiceContract]
   2:  public interface IDemoService
   3:  {
   4:      [OperationContract]
   5:      IEnumerable<string> GetItems(string category);
   6:  }

下面是实现了该契约接口的DemoService的实现:GetItems方法返回一个包含3个字符串的集合,但是在返回之前我们需要对参数实施验证。如果category参数提供的字符串为Null或者是空字符串,抛出一个FaultException异常并提示“Invalid Category”,这样客户端在输入不合法参数的情况下可以得到错误消息。这样的编程方式再正常不过了,不是吗?

public class DemoService : IDemoService
{
    public IEnumerable<string> GetItems(string categoty)
    {
        if (string.IsNullOrEmpty(categoty))
        {
            throw new FaultException("Invalid category");
        }
        yield return "Foo";
        yield return "Bar";
        yield return "Baz";
    }
}

可是正常并不意味着正确,客户端其实根本无法得到服务端提供给它的错误消息,如下所示的是客户端调用服务时指定一个空字符串参数情况下得到的错误。一个CommunicationException异常被抛出来,得到的错误消息为“An error occurred while receiving the HTTP response tohttp://127.0.0.1:3721/demoservice. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details.”

image

这貌似和我们预期的效果不一样,我们希望的是客户端抛出一个FaultException,并提示“Invalid category”。这实际上就是因为“yield”在作祟,不相信的话可以将定义在DemoService的GetItems方法替换成如下的定义,即直接返回一个string[]对像。

public class DemoService : IDemoService
{
    public IEnumerable<string> GetItems(string categoty)
    {
        if (string.IsNullOrEmpty(categoty))
        {
            throw new FaultException("Invalid category");
        }
        return new string[] { "Foo", "Bar", "Baz" };
    }
}

再次运行我们的程序,这回可以得到我们期望的结果了。

image

有兴趣的朋友可以思考一下为什么两种貌似等效的方式为何会出现完全不同的结果,具体原因请看[下篇]。

yield在WCF中的错误使用——99%的开发人员都有可能犯的错误[上篇] 
yield在WCF中的错误使用——99%的开发人员都有可能犯的错误[下篇]



昨天写了《yield在WCF中的错误使用——99%的开发人员都有可能犯的错误[上篇]》,引起了一些讨论。关于yield关键字这个语法糖背后的原理(C#编译器将它翻译成什么)其实挺简单,虽然有时候因为误用它会导致一些问题,但是它本无过错。接下来,我们通过这篇短文简单地谈谈我所理解的yield。

目录 
一、先看一个简单的例子 
二、了解本质,只需要看看yield最终编译成什么 
三、回到WCF的例子

一、先看一个简单的例子

我们现在看一个简单的例子。我们在一个Console应用中编写了如下一段简单的程序:返回类型为IEnumerable<string>的方法GetItems以yield return的方式返回一个包含三个字符串的集合,而在方法开始的时候我们打印一段文字表明定义在方法中的操作开始执行。在Main方法中,我们先调用GetItems方法将“集合对象”返回,然后调用其ToArray方法。在调用该方法之前我们打印一段文字表明对集合对象进行迭代。

static void Main(string[] args)
{
    IEnumerable<string> items = GetItems();
    Console.WriteLine("Begin to iterate the collection.");
    items.ToArray();
}

static IEnumerable<string> GetItems()
{
    Console.WriteLine("Begin to invoke GetItems() method");
    yield return "Foo";
    yield return "Bar";
    yield return "Baz";
}

对于上面这段代码,我想肯定有人会认为得到的结果应该是这样:

Begin to invoke GetItems() method
Begin to iterate the collection.

但是下面才是真正的执行结果。也就是说,一旦我们在一个返回类型为IEnumerable或者IEnumerable<T>的方式中通过yield return返回集合元素,意味着这个定义在方法中操作会被“延后执行”——操作的真正执行不是发生在方法调用的时候,而是延后到对返回的集合进行迭代的时候。我们大体可以以这样的方式来“解释”这个现象:一旦我们使用了yield return,返回元素的操作会被封装成“可执行的表达式”的方式返回,一旦我们对集合进行迭代的时候,这些表达式才会被执行。

Begin to iterate the collection.
Begin to invoke GetItems() method

 

二、了解本质,只需要看看yield最终编译成什么

上面我们通过“延迟执行”和“可执行表达式”的形式来解释yield return,仅仅是为了比较好地理解它所体现出来的效果而已,实际上并没有这回事,这与LINQ的延迟加载更不是一回事。yield return仅仅是C#的一个语法糖而已,是编译器玩的一个小花招。如何透过这一层“糖纸”看到本质的东西,只需要看看编译器最终编译后的与之等效的代码是什么样子就可以了。对于上面这个例子来说,不管GetItems方法中以何种方式返回需要的对象,返回值总归是一个实现了IEnumerable <string>接口的某个类型的对象,我们只需要看看这个类型具有怎样的定义就知道C#编译器如果来“解释”yield return。

我们可以直接利用Reflector打开编译后的程序集,然后将.NET Framework的版本调成1.0(不支持C#针对后续版本提供的语法糖),这样就可以以“本质”的方式查看我们编写的代码了。如下面的代码片段所示,GetItems方法中没有发现我们定义的代码,而是直接返回一个类型为<GetItems>d__0的对象,看到这里相信读者朋友们知道为什么执行GetItems方法的时候并没有文字输出的真正原因了吧。

internal class Program
{
    private static IEnumerable<string> GetItems()
    {
        return new <GetItems>d__0(-2);
    }
    private sealed class <GetItems>d__0 : IEnumerable<string>, IEnumerable, IEnumerator<string>, IEnumerator, IDisposable
}

<GetItems>d__0是自动生成的类型,它实现了IEnumerable<string>接口,也实现了IEnumerator<string>,其 GetEnumerator()方法返回的实际上就是他自己。至于对<GetItems>d__0对象的进行迭代的时候如何返回具体元素,只要看看该类型的定义就一目了然了。如下面的代码片段所示,集合元素的返回实现在MoveNext()方法中,方法开始的操作(Console.WriteLine("Begin to invoke GetItems() method"))发生在第一次迭代的时候。

private sealed class <GetItems>d__0 : IEnumerable<string>, IEnumerable, IEnumerator<string>, IEnumerator, IDisposable
{
    private int <>1__state;
    private string <>2__current;

    private bool MoveNext()
    {
        switch (this.<>1__state)
        {
            case 0:
                this.<>1__state = -1;
                Console.WriteLine("Begin to invoke GetItems() method");
                this.<>2__current = "Foo";
                this.<>1__state = 1;
                return true;

            case 1:
                this.<>1__state = -1;
                this.<>2__current = "Bar";
                this.<>1__state = 2;
                return true;

            case 2:
                this.<>1__state = -1;
                this.<>2__current = "Baz";
                this.<>1__state = 3;
                return true;

            case 3:
                this.<>1__state = -1;
                break;
        }
        return false;
    }
    string IEnumerator<string>.Current
    {
        [DebuggerHidden]
        get
        {
            return this.<>2__current;
        }
    }

    object IEnumerator.Current
    {
        [DebuggerHidden]
        get
        {
            return this.<>2__current;
        }
    }
}

 

三、回到WCF的例子

再次回到《yield在WCF中的错误使用——99%的开发人员都有可能犯的错误[上篇]》中提到的例子,现在来解释为什么针对如下两段代码,前者抛出的异常不能被WCF正常处理,而后者可以。原因很简单——两段代码抛出异常的时机是不一样的。对于后者,异常在执行GetItems方法的时候会立即抛出来,WCF会捕获这个异常并作为应用级别的异常进行正常处理;对于前者,通过上面的分析我们知道异常实际上发生在对返回“集合对象”进行迭代的时候。具体是什么时候呢?其实就是对返回对象进行序列化的时候,此时抛出的异常将将会视为系统异常来处理。

public class DemoService : IDemoService
{
    public IEnumerable<string> GetItems(string categoty)
    {
        if (string.IsNullOrEmpty(categoty))
        {
            throw new FaultException("Invalid category");
        }
        yield return "Foo";
        yield return "Bar";
        yield return "Baz";
    }
}

public class DemoService : IDemoService
{
    public IEnumerable<string> GetItems(string categoty)
    {
        if (string.IsNullOrEmpty(categoty))
        {
            throw new FaultException("Invalid category");
        }
        return new string[] { "Foo", "Bar", "Baz" };
    }
}

我个人觉得这是WCF值得改进的地方,但是目前来说为了避免这样的问题,我推荐将WCF契约接口操作方法中的返回类型定义成数组,而不是IEnumerable或者IEnumerable<T>(顺便说一下,WCF针对Array、List以及其他集合类型的序列化/反序列化行为是一致的),但是我个人对IEnumerable或者IEnumerable<T>不排斥。

yield在WCF中的错误使用——99%的开发人员都有可能犯的错误[上篇] 
yield在WCF中的错误使用——99%的开发人员都有可能犯的错误[下篇]


使用 MATLAB/Simulink 搭建一个步进电机细分控制系统的仿真模型 步进电机(Stepper Motor)是一种将电脉冲信号转换为角位移或线位移的执行元件,广泛应用于数控机床、3D打印、机器人、精密仪器等领域。其特点是开环控制、定位精度高、响应快、无累积误差。然而,传统步进电机在整步或半步运行时存在振动大、噪音高、转矩波动明显等问题。为了解决这些问题,细分控制技术被广泛采用。细分控制通过对绕组电流进行精确控制,使转子在两个整步之间实现平滑移动,从而提升电机的运行平稳性和位置精度。本教程将带你使用搭建一个步进电机细分控制系统的仿真模型,并通过仿真分析其。 阅读详情

相关推荐

手把手教你打造CTF动态靶场

公司想搞一个CTF比赛,需要一个竞赛靶场,于是这个任务就落在了我的身上,平台是利用CTFd框架,基于docker搭建的动态靶场,搭建过程中遇到的坑比较多,这里我会把每一个遇到的坑都指出来。我搭建的是CTFd的3.X的版本。第一次搭建的话,搭建的过程会有些痛苦,但是搭建完成后会越来越熟练,这个平台的可用性还是很高的。由于搭建的过程中遇到的坑太多了,各种各样的问题,所以决定写下这篇文章,把所有的坑都指出来,也算为后边想搭这个平台的小伙伴指一条路。Taps:资源来源:星海安全实验室如果你也对网络安全&黑客。

Python栈 4932

C# 基础小知识之yield 关键字

对于yield关键字我们首先看一下msdn的解释: 如果你在语句中使用yield关键字,则意味着它在其中出现的方法、运算符或get访问器是迭代器。通过使用yield定义迭代器,可在实现自定义集合类型的IEnumerable和IEnumerator模式时无需其他显式类(保留枚举状态的类,有关示例,请参阅IEnumerator<T>)。 yield是一...

aori3524的博客 230

QGIS:安装Deepness插件+应用

简单的描述了deepness的安装应用历程,重点是记录博主本次遇到的坑

fengyi157的博客 2331

四元数、协程、Resources同步异步加载资源、异步场景切换、XML周学习总结

万向节死锁是指在使用三个欧拉角表示旋转时,如果其中一个旋转使得另外两个旋转轴对齐,那么就会失去一个自由度,导致无法表示出原来可以通过三个独立旋转实现的某些旋转状态。简而言之,就是在特定的旋转状态下,我们无法使用欧拉角独立控制物体的所有三个旋转方向,好像物体被锁在了某个旋转平面。

jianhenhen的博客 1060

基于LangGraph的智能报告生成平台项目分析

不知道你是否知道或者了解OpenAI and Gemini Deep Research。他们是一种能够根据输入问题进行规划、结合网络搜索获取信息并最终呈现结果的研究工具或技术。那这样research是如何实现的呢?最近刚好看到一个实现类似功能的开源项目: open_deep_search。本文将基于该项目进行代码层面剖析。

u011160113的博客 923

思科模拟器实战:MQTT协议实现物联网设备通信全流程解析

本文详细解析了如何在思科模拟器中利用MQTT协议实现物联网设备通信的全流程。通过搭建网络拓扑、配置MQTT Broker与客户端,并编写发布/订阅逻辑,手把手指导读者完成一个“开关控制电灯”的经典物联网场景实验,帮助零成本掌握物联网通信核心。

weixin_29294975的博客 381

【C#】yield语法糖作用和原理(迭代器IEnumerable和IEnumerator)

代码发在网盘,可以自己测一下. 里面还有IEnumerable版本(实现一个获取IEnumerator的方法就可以)和Foreach调用(自动移MoveNext+拿Current,直到没有下一个)版本。把yield return 上下文的实现放入 MoveNext()中去, MoveNext返回false代表迭代完成,没有下一个元素了.可以看到yield return 的值去了Current, 而其前后的代码分别去了 MoveNext()的上下2块代码中去.百度GPT, 免费,理解能力低于GPT4。

neoiw的博客 381

微信小程序 - 真机调试调用接口报错{errno: 600001, errMsg: “request:fail -102:net::ERR_CONNECTION_REFUSED},真机运行测试请求异常

微信小程序,真机,调试测试,运行报错,API接口,服务器接口都报错了,小程序报错errno: 600001,小程序报错request:fail -102:net::ERR_CONNECTION_REFUSED,真实手机打开接口全都失效了,微信小程序真机测试失败,微信小程序{errno: 600001, errMsg: "request:fail -102:net::ERR_CONNECTION_REFUSED"},详细解决方法,https,服务器SSL证书,无法解决,服务器更换SSL证书,接口地址改了也不行

🏆 前端领域优质创作者 1万+

协程coroutine 二、常用的几种coroutine操作

  1、yield return null:等到下一帧继续执行 2、yield return newwaitforendofframe:这帧结束后继续执行 3、yield return newwaitforfixedupdates:等到下一物理帧后执行 4、yield return newwaitforseconds:N秒后继续执行。实际应用:定时器。当然,也可以使用update和invo...

星宇的专栏 1434

【软件定义汽车】-【架构篇】-全面讲解整车电子电气架构演进

前言: 前段时间,陆陆续续写了一些电子电气架构相关的文章。这里打算再写最后一篇作为收尾,为整个系列画上句号,把坑填上,这个话题就算过去了。后续一段时间,尽量把之前文章中挖的“坑”填一下。行文逻辑将借鉴5W2H进行。 另外,本文作为一篇整合性的文章,为保证逻辑的完整性,主要是将大量过去自己文章中的内容进行梳理整合,也存在很多行业内专家的公开文章的内容,请见谅。 WHAT1:什么是汽车电子电气架构? 电子电气架构:EEA,Electrical/Electronic Architecture.

零点壹客的博客 1万+

本文将详细介绍如何在 Qt (C++) 应用程序中集成 osgEarth,加载三维数字地球,并在指定经纬度添加三维模型(如飞机、建筑等)。

OSG 与 Qt 的交互通常需要处理 OpenGL 上下文。osgEarth 3.x 提供了对 Qt 的较好支持,或者可以使用。在普通 OSG 程序中,你需要手动处理坐标系转换将经纬度转为笛卡尔坐标 (XYZ)。这是实现的关键,将 OSG 的渲染循环集成到 Qt 的事件循环中。第三方库,但在本教程中,我们将展示一种通过继承。,将 Qt 的输入事件转换为 OSG 理解的。事件,这是实现交互(旋转地球、缩放)的基础。来嵌入 OSG 视图的标准方法。在这里我们初始化地球并添加模型。节点简化了这一过程。

qq_45859163的博客 1044

unity-协程详解

unity协程详解

qq_52905520的博客 4908

《Java开发及前端最全面试题-Offer直通车》目录

Java开发是需求最广,工资高的细分领域,同时也是会的人最多,有的公司招1个Java开发,要面试几十甚至上百面试者,同时要求很高,要求通晓技术栈很多及很深深度。如果你正准备面试,想要脱颖而出,那么《Java开发及前端最全面试题-Offer直通车》是你的不二选择。本书汇集了多篇超过1万字的精华内容,无论是Java基础、数据库、SpringBoot和SpringCloud等必问的,还是前端、k8s等容器和虚拟化和大数据各类技术面试,本书都为你提供了最全面的试题和参考答案。

大模型大数据攻城狮的专栏 1178

C#的yield语法糖研究(转载)

研究C#的yield语法作用,以及其内部实现原理

myqTmac的博客 365

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

yield用法 (转载)

yield是什么 yield是ES6的新关键字,使生成器函数执行暂停,yield关键字后面的表达式的值返回给生成器的调用者。它可以被认为是一个基于生成器的版本的return关键字。 yield关键字实际返回一个IteratorResult(迭代器)对象,它有两个属性,value和done,分别代表返回值和是否完成。 yield无法单独工作,需要配合generator(生成器)的其他函数,如n...

DeBruyne的博客 1337

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

【转载】yield

yield类似于return,在函数中返回值给调用者使用了yield的函数是有状态的函数,后面的调用会基于前面的调用状态继续执行。

yongjie 649

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

PHP 中不可不知的语法糖,你都用了哪些?

在PHP中,有许多语法糖可以简化代码编写,提高可读性和开发效率。下面我将详细介绍一些我认为每个PHP开发者都应该知道的语法糖,并分享我是如何在实际开发中使用它们的。

Java程序员_编程开发学习笔记_网站安全运维教程_渗透技术教程 659

C#的yield语法糖研究

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.youkuaiyun.com/myqTmacc/article/details/70517909 ...

Andrewniu的博客 528

yield 跟 yield * 的区别

yield表达式的值,是下一个iter.next的参数值 yield*表达式的值,是yield*后的iterable在done为true时的value值。 yield * 的使用: function* outer() { yield 'open' yield inner() yield 'close'}function* inner() { yield 'he...

dd1234562014的博客 1764

C#语法糖yield

代码中经常遇到迭代数据集合的情况,当希望获取到一个IEnumerable<T>类型的集合,而又不想把数据一次性加载到内存中时, 可以考虑使用yield,yield关键字可实现用户的按需获取数据,延时加载数据。 举例说明在实现同一个功能时,使用yield和不使用yield的区别: 1.在不使用yield关键字迭代数据时代码如下: public static IEnumera...

weixin_30344795的博客 226

Python中yield和yield from区别

本文不是介绍yield的使用的,仅仅是介绍yield和yield from两者区别的。 如果直接说两者的区别,估计不太明白,下面使用一个用例来说明两者之间的区别 def gen(*args, **kwargs): for item in args: yield item def gen2(*args, **kwargs): for item in args: ...

weixin_33716154的博客 233

【转载】Python yield用法

首先我要吐槽一下,看程序的过程中遇见了yield这个关键字,然后百度的时候,发现没有一个能简单的让我懂的,讲起来真TM的都是头头是道,什么参数,什么传递的,还口口声声说自己的教程是最简单的,最浅显易懂的,我就想问没有有考虑过读者的感受。 接下来是正题: 首先,如果你还没有对yield有个初步分认识,那么你先把yield看做“return”,这个是直观的,它首先是个return,普通的return是...

maqian5的博客 214

(转载)c# yield return用法

yield return null; // 下一帧再执行后续代码 yield return 0; //下一帧再执行后续代码 yield return 6;//(任意数字) 下一帧再执行后续代码 yield break; //直接结束该协程的后续操作 yield return asyncOperation;//等异步操作结束后再执行后续代码 yield return StartCoroution(/*某个协程*/);//等待某个协程执行完毕后再执行后续代码 yield return WWW();//等待WWW

MagicianThirteen 811

python2中有yield_【转载】关于Python中的yield

在介绍yield前有必要先说明下Python中的迭代器(iterator)和生成器(constructor)。一、迭代器(iterator)在Python中,for循环可以用于Python中的任何类型,包括列表、元祖等等,实际上,for循环可用于任何“可迭代对象”,这其实就是迭代器迭代器是一个实现了迭代器协议的对象,Python中的迭代器协议就是有next方法的对象会前进到下一结果,而在一系列结果...

weixin_31610297的博客 111

Python yield 使用浅析【转载】

转自:https://www.ibm.com/developerworks/cn/opensource/os-cn-python-yield/     您可能听说过,带有 yield 的函数在 Python 中被称之为 generator(生成器),何谓 generator ? 我们先抛开 generator,以一个常见的编程题目来展示 yield 的概念。 如何生成斐波那契數列 斐波...

qq_41607306的博客 187

1、【转载】Python yield 使用浅析

1、【转载】Python yield 使用浅析 【注:此文转载自https://www.ibm.com/developerworks/cn/opensource/os-cn-python-yield/】 您可能听说过,带有 yield 的函数在 Python 中被称之为 generator(生成器),何谓 generator ? 我们...

weixin_33910137的博客 951
上一篇: Ubuntu 12.10 下进行NDK 编译出错解决
下一篇: Unity3D coroutines in detail
垃圾桶
博客等级 码龄15年 20粉丝 18原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值