使用Linq扩展方法时容易忽略的小问题

问题重现

下面直接给出用于说明文章主题的完整代码。

//************************************************************
//
// Linq扩展方法示例代码
//
// Author:三五月儿
//
// Date:2014/10/01
//
// http://blog.csdn.net/yl2isoft
//
//************************************************************
using System;
using System.Collections.Generic;
using System.Linq;
namespace LinqExtendFunctionExp
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Student> studentList = new List<Student>();
            studentList.Add(new Student() { Id = 1, Name = "ZhangSan", ClassId = 1, Score = 85});
            studentList.Add(new Student() { Id = 2, Name = "LiSi" , ClassId = 2, Score = 76});
            studentList.Add(new Student() { Id = 3, Name = "WangWu" , ClassId = 1, Score = 89});
            studentList.Add(new Student() { Id = 4, Name = "ZhaoLiu", ClassId = 2, Score = 91 });
            studentList.Add(new Student() { Id = 5, Name = "LiuJian" , ClassId = 1, Score = 78});
            studentList.Add(new Student() { Id = 6, Name = "WuBin", ClassId = 2, Score = 67 });
            var handledStudentList = studentList.GroupBy(it => it.ClassId).OrderBy(it => it.Max(p => p.Score)).ToList();
            foreach (var students in handledStudentList)
            {
                foreach(var student in students)
                {
                    if (student.Score < 85)
                    {
                         student.Name = "不合格";
                     }
                }
            }
            foreach (var s in studentList)
            {
                Console.WriteLine("Id = {0}, Name = {1}, ClassId = {2}, Score = {3}", s.Id, s.Name, s.ClassId, s.Score);
            }
        }
    }
    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int ClassId { get; set; }
        public int Score { get; set; }
    }
}

代码主要完成的工作是:

(1)定义学生对象集合studentList;

(2)使用Linq扩展方法对studentList集合进行分组和排序操作得到集合handledStudentList;

(3)遍历handledStudentList集合中的学生对象,当学生的成绩低于85时,修改该学生的姓名为“不合格”;

(4)输出studentList集合中所有学生的信息。

本文需要探讨的问题是:

在操作(4)中输出studentList集合学生信息时,Id为2,5,6的学生成绩低于85分,那么这些学生的姓名会被显示成“不合格”吗?

你可能会说不会,同时给出你的原因:

因为将学生姓名修改为“不合格”的操作是在遍历handledStudentList集合时完成的,因此修改的是handledStudentList集合中的学生信息,所以输出studentList集合中学生信息时不会受此影响。

但是,实际情况会是这样吗?请看代码的执行结果。

图1 代码运行结果

从图1可以了解到:学号为2,5,6的学生姓名被修改为了“不合格”,原因出在哪里呢?

问题分析

下面以扩展方法GroupBy为例来对问题进行分析,使用Linq中其他扩展方法的情况与使用扩展方法GroupBy的情况类似。

扩展方法GroupBy的作用是:按照指定的条件对集合进行分组操作,返回一个GroupedEnumerable类型的对象,在返回的对象内部仍然持有被分组集合的引用,所以,当我们操作分组后的集合时其实操作的仍然是分组前的集合对象。正因为此,才出现了本文前面描述的现象。

对于这点,是不是很容易被忽视啊,因为明明返回了一个新类型的对象,谁会想到,在它内部还持有被处理集合的引用呢。

好吧,希望大家在以后使用Linq的扩展方法时能注意这点。

至于GroupBy方法为什么会这样实现,那就得去研究GroupBy方法的源码了,等到以后有时间再说吧,这里只是想提出这样一个问题供大家参考,以免因为不知道这个知识点而出现一些本不应该出现的小问题。

好了,88。

时间: 2024-12-15 03:55:53

使用Linq扩展方法时容易忽略的小问题的相关文章

LinQ—扩展方法

概述 本节主要讲解扩展方法,涉及LinQ的具体知识不多. 扩展方法的描述 .net framework为编程人员提供了很多的类,很多的方法,但是,不论.net framework在类中为我们提供了多么多的方法,有时候仍然不能满足我们的需求,例如:你想让字符串对象具有ToPascal方法,含义就是将字符串转化为Pascal格式,并返回,我们知道,.net framework提供的String类中并没有为我们提供相应的方法,此时,我们应该怎么做才可以达到我们的目的呢?有人说可以继承String类,这

ToDictionary()LINQ扩展方法

ToList() 使用IEnumerable<T>并将其转换为 List<T>,那么 ToDictionary()也是类似的.大多数情况ToDictionary()是一个非常方便的方法,将查询的结果(或任何 IEnumerable<T>)转换成一个Dictionary<TKey,TValue>. 关键是您需要定义T如何分别转换TKey和TValue. 如果说我们有超级大的产品列表,希望把它放在一个Dictionary<int, product>,

Linq扩展方法获取单个元素

在使用Linq 提供的扩展方法时,First(OrDefault), Single(OrDefault), Last(OrDefault)都具有返回单个元素的功能.MSDN对这些方法的描述只有功能说明,没有关于内部的相关实现的描述说明. 首先我们来看下MSDN上关于这些扩展方法的官方描述: First: 返回序列中的第一个元素 . FirstOrDefault: 返回序列中的第一个元素:如果未找到元素,则返回默认值. Last:返回序列的最后一个元素. LastOrDefault: 返回序列中的

ABP框架源码中的Linq扩展方法

文件目录:aspnetboilerplate-dev\aspnetboilerplate-dev\src\Abp\Collections\Extensions\EnumerableExtensions.cs using System; using System.Collections.Generic; using System.Linq; namespace Abp.Collections.Extensions { /// <summary> /// Extension methods for

【手记】走近科学之为什么JObject不能调用LINQ扩展方法

Json.NET的JObject明明实现了IEnumerable<T>,具体来说是IEnumerable<KeyValuePair<string, JToken>>,按说JObject类型的对象是可以直接调用Select.Where等linq扩展方法的,但偏偏就是不行,代码如下: using System.Linq; ... var jobj = new JObject(); var xxx = jobj.Select(x=>x); //报错:JObject未包含

Linq入门——什么是linq &amp; 扩展方法

一,什么是Linq linq(language integrated Query):语言集成查询: linq包含如下: 对对象的查询,对数据库的查询,对XML的查询. 那么,没有linq前我们是怎样查询的? 先看一个例子: 现在我们要查询大于50的数,: 在没有linq之前,我们的代码时这样的: 使用了linq查询: 首先,从直观上看,代码更加简洁,其次,对于查询部分,接近SQL语句,层次清晰,容易理解: 除了简单高效以为,LINQ的出现解决了很多问题: 1,面向对象与数据访问两个领域长期分裂,

c# 如果一个对象的值为null,那么它调用扩展方法时为甚么不报错

如果一个对象的值为null,那么它调用扩展方法时会报错吗? Person p = null ; p.ExtendMethod(); 上述代码出现的情况不会报错,刚开始遇到这种情况时很纳闷,就去问了大牛.大牛解释如下: 扩展函数其实只是为了让代码更具有可读性, 但最终在clr中会翻译成标准的静态函数调用, 比如: public static void ExtMethod(this string str) { if(!string.IsNullOrEmpty(str)) { //对str处理 } }

Linq扩展方法之All 、Any

// Summary: // 确定序列中的所有元素是否满足条件. // Parameters: // source:包含要应用谓词的元素的 System.Collections.Generic.IEnumerable`1. // predicate:用于测试每个元素是否满足条件的函数. // Type parameters: // TSource:source 中的元素的类型. // Returns:如果源序列中的每个元素都通过指定谓词中的测试,或者序列为空,则为 true:否则为 false.

Linq扩展方法

1.All()如果数据中所有条目斗鱼谓词匹配返回true 2.Any()至少有一条匹配返回true 3.Contains:指定条目或值返回true 4.count:返回条目数量 5.First:返回第一条条目 6.FirstOrDefault:返回第一条条目,若没有时返回默认值 7.last:返回最后一条条目 8.lastOrdefault:返回最后一条条目,若没有时返回默认值 9.max:返回最大值 10.min:返回最小值 11.orderBy:对数据进行排序 12.Reverse:反转数据