• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >C#教程 > C#中的IEnumerable接口深入研究

C#中的IEnumerable接口深入研究

作者: 字体:[增加 减小] 来源:互联网 时间:2017-05-28

通过本文主要向大家介绍了c#ienumerable,c#中ienumerable,ienumerable接口,ienumerable,ienumerable用法等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

C#和VB.NET中的LINQ提供了一种与SQL查询类似的“对象查询”语言,对于熟悉SQL语言的人来说除了可以提供类似关联、分组查询的功能外,还能获取编译时检查和Intellisense的支持,使用Entity Framework更是能够自动为对象实体的查询生成SQL语句,所以很受大中型信息系统设计者的青睐。

IEnumerable这个接口可以说是为了这个特性“量身定制”,再加上微软提供的扩展(Extension)方法和Lambda表达式,给开发者带来了无穷的便利。本人在最近的开发工作中使用了大量的这种特性,同时在调试过程中还遇到了一个小问题,那么正好趁此机会好好研究一下相关原理和实现。

先从一个现实的例子开始吧。假如我们要做一个商品检索功能(这只是一个例子,我当然不可能把公司的产品也业务在这里贴出来),其中有一个检索条件是可以指定厂家的名称并进行模糊匹配。厂家的包括两个名称:注册名称和一般性名称,我们只按一般性名称进行检索。当然你可以说直接用SQL查询就行了,但是我们的系统是以实体对象为核心进行设计的,厂家的数量也不会太多,大概1000条。为了不增加系统的复杂性,只考虑使用现有的数据访问层接口进行实现(按过滤条件获取商品,以及获取所有厂商),这时LINQ的便捷性就体现出来了。

借助IEnumerable接口和其辅助类,我们可以写出以下代码:
public GoodsListResponse GetGoodsList(GoodsListRequest request)
{
    //从数据库中按商品类别获取商品列表
    IEnumerable<Goods> goods = GoodsInformation.GetGoodsByCategory(request.CategoryId);

    //用户指定了商品名检索字段,进行模糊匹配
    //如果没有指定,则不对商品名进行过滤
    if (!String.IsNullOrWhiteSpace(request.GoodsName))
    {
        request.GoodsName = request.GoodsName.Trim().ToUpper();
       
        //按商品名对 goods 中的对象进行过滤
        //生成一个新的 IEnumerable<Goods> 类型的迭代器
        goods = goods.Where(g => g.GoodsName.ToUpper().Contains(request.GoodsName));
    }

    //如果用户指定的厂商的检索字段,进行模糊匹配
    if (!String.IsNullOrWhiteSpace(request.ManufactureName))
    {
        request.ManufactureName = request.ManufactureName.Trim().ToUpper();

        //只提供了获取所有厂商的列表方法
        //取出所有厂商,筛选包含关键字的厂商
        IEnumerable<Manufacture> manufactures = ManufactureInformation.GetAll();
        manufactures = manufactures.Where(m => m.Name.GeneralName.ToUpper()
                            .Contains(request.ManufactureName));

        //取出任何符合所匹配厂商的商品
        goods = goods.Where(g => manufactures.Any(m => m.Id == g.ManufactureId));
    }

    GoodsListResponse response = new GoodsListResponse();

    //将 goods 放到一个 List<Goods> 对象中,并返回给客户端
    response.GoodsList = goods.ToList();

    return response;
}
</div>

假如不使用IEnumerable这个接口,所实现的代码远比上面复杂且难看。我们需要写大量的foreach语句,并手工生成很多中间的 List 来不断地筛选对象(你可以尝试把第二个if块改写成不用IEnumerable接口的形式)。

看上去一切都很和谐,但是上面的代码有一个隐含的bug,这个bug也是今天上午困扰了我许久的一个问题。

运行程序,当我不输入厂商检索条件的时候,程序运行是正确的。但当我输入一个厂商的名字时,系统抛出了一个空引用的异常。咦?为什么会有空引用呢?我输入的厂商是数据库中不存在的厂商,因此我觉得问题可以出在goods = goods.Where(g => manufactures.Any(m => m.Id == g.ManufactureId)) 这句话上。既然manufactures是空的,那么是不是意味着我不能调用其 Any 方法呢(lambda表达式中的部分)。于是我改写成以下形式:
if (manufactures != null)
    //取出任何符合所匹配厂商的商品
    goods = goods.Where(g => manufactures.Any(m => m.Id == g.ManufactureId));
</div>

还是不行,那么我对manufactures判断其是否有元素,就调用其无参数的Any方法,这时问题依旧:

聪明的你肯定已经看出问题出在哪了,因为Visual Studio已经提示得很清楚了。但我当时还局限在“列表为空”这个框框中,因此迟迟不能发现原因。出错是发生在 manufactures.Any() 这句话上,而我已经判断了它不为空啊,为什么还会抛错呢?

后来叫了一个同事帮我看,他说的四个字一下子就提醒了我“延迟计算”。哦,对!我怎么把这个特性给忘了。在最初的代码中(就是没有对 manufactures 为空进行判断),出错是发生在 goods.ToList() 这句话时,而图上的那个代码段出错是发生在调用Any()方法时(图中的灰色部分),而我单步跟踪到 Any() 这句话上时,出错的语句跳到 Where 子句(黄色部分),说明知道访问 Any 方法时lambda表达式才被调用。

那么很显然是 Where 语句中这个 predicate 有问题:Manufacture的Name字段可能为空(数据库中存在这样的数据,所以导致在 translate 的时候Name字段为空),那么改写成以下形式就能解决问题,当然我们不用对 manufactures 列表进行为空的判断:
manufactures = manufactures.Where(m => m.Name != null &&
                    m.Name.GeneralName.ToUpper().Contains(request.ManufactureName));
</div>
在此要感谢那位同事看出了问题所在,否则我不知道还得郁闷多久。

我之前在使用 LINQ 语句的时候知道它的延迟计算特性,但是没有想到从根本上自 IEnumerable 的扩展方法就有这个特性。那么很显然,C#的编译器只是把 LINQ 语句改写成类似于调用 Where、Select之类的扩展方法,延迟计算这种特性是 IEnumerable 的扩展方法就支持的!我之前一直以为我每调用一次 Where 或者 Select(其实我SelectMany用得更多),就会对结果进行过滤,现在看来并不是这样。

即使是使用 Where 等扩展方法, 执行这些 predicate 的时间是在 foreach 和 ToList 的时候才发生。

为什么会这样呢?看样子这完全不应该呀?Where子句的返回值就是一个IEnumerable的迭代器,按道理应该已经筛选了对象啊?为了彻底搞清楚这个问题,那么方法很明显——看 .NET 的源代码。

Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) 是它的方法头,在看源代码之前,相信你已经知道微软大概是怎么实现的了:既然Where接受一个Func类型的委托,并且都是在ToList 或者 foreach 的时候计算的,那么显而易见实现应该是……

好了,来看下代码吧。IEnumerable的扩展方法都在 Enumerable 这个静态类中,Where方法的实现代码如下:
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
    if (source == null) throw Error.Argu

分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • C#中IEnumerable、ICollection、IList、List之间的区别
  • C#中IEnumerable接口用法实例分析
  • C#中的IEnumerable简介及简单实现实例
  • C#中的IEnumerable接口深入研究
  • 基于C#中IDisposable与IEnumerable、IEnumerator的应用

相关文章

  • 2017-05-28C#创建、读取和修改Excel的方法
  • 2017-05-28利用C#实现分布式数据库查询
  • 2017-05-28C#中实现线程同步lock关键字的用法详解
  • 2017-05-28C#多线程学习之(四)使用线程池进行多线程的自动管理
  • 2017-05-28C#使用round函数四舍五入的方法
  • 2017-05-28C#反射之基础应用实例总结
  • 2017-05-28C# memcache 使用介绍
  • 2017-05-28WinForm特效之桌面上的遮罩层实现方法
  • 2017-05-28c#桥接模式(bridge结构模式)用法实例
  • 2017-05-28C# PLINQ 内存列表查询优化历程

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • c#生成excel示例sql数据库导出excel
    • C#汉字转换拼音技术详解(高性能)
    • C#实现远程连接ORACLE数据库的方法
    • C#中string.format用法详解
    • C#编程中使用ref和out关键字来传递数组对象的用法
    • C#实现绘制面形图表的方法详解
    • C#中文件名或文件路径非法字符判断方法
    • C# partial关键字说明
    • C# 线程同步详解
    • c#反射调用方法示例

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有