C#高级语法:LINQ 查表的常用十大基本技巧及其拓展技巧

该博客介绍了C#中LINQ的常用扩展方法,包括基本十招、复杂排序、集合计算、类型批量转化、字段提取等。如获取列表用.where(条件).Tolist(),获取实体用FirstOrDefault(条件),还提及了ForEach写法、remove方法等注意事项,以及不同场景下的操作技巧。
该文章已生成可运行项目,

目录

一、基本十招

二、复杂排序

三、集合计算(交并差+父集子集)

1.交并差集

2.父集子集

四、类型的批量转化(字段映射)

1、非引用类型

2、引用类型

3、List转换

五、字段提取和字段子项提取

六、用Any和All做元素规则判断

八、重复元素 & 去重

九、列表的相互嵌套查询

十、列表拆分(分页)

更多扩展方法


一、基本十招

【总结】

1.获取列表用.where(条件).Tolist()

2.获取实体用FirstOrDefault(条件)

3.如果没有该方法,请先.Tolist(),因为大多数都是列表的方法


class MainClass
{
    static List<Person> personList = new List<Person>()
    {
        new Person(){ Id=1,Name="小明",Age=20,Score=100},
        new Person(){ Id=2,Name="小王",Age=40,Score=80},
        new Person(){ Id=3,Name="小刘",Age=60,Score=60},
    };
    static List<Person> personList2 = new List<Person>()
    {
        new Person(){ Id=4,Name="小测试",Age=12,Score=90},
    };

    static void Main(string[] args)
    {

        //一、查找数据
        //(1)查找年龄=40的实体列表
        var age40List = personList.Where(p => p.Age == 40).ToList();//where是惰性求值,若不加ToList使用时才会实际执行筛选

        //(2)查找查找年龄=40的第一条数据的名字,存入字符串(?.表示不为空才点出实体,以防报错)
        var age40Name = personList.FirstOrDefault(p => p.Age == 40)?.Name;

        //(3)查找ID在ID列表的数据
        List<int> listID = new List<int>() { 2, 4 };
        var personListInID = personList.Where(x => listID.Contains(x.Id)).ToList();//查询ID=2或4的所有实体

        //(4)寻找实体的索引和寻找索引的实体
        int index = personList.FindIndex(x => x.Age == 40);//找出Age=40的索引位置
        Person p1 = personList[2];//找出索引=2的实体【不推荐】
        Person p2 = personList.ElementAtOrDefault(2);//找出索引=2的实体【推荐:找不到返回null】

        //2.分页查找(若超出索引,GetRange会报错,skip不会)
        var pageList = personList.Skip(1).Take(2);//跳过1条数据取2条数据(即从索引1开始取2条数据)
        var rangeList = personList.GetRange(1, 2);//从索引1开始取2条数据

        //二、映射数据
        // 查找年龄=40的人的姓名列表
        var nameList = personList.Where(p => p.Age == 40).Select(p => p.Name).ToList();
        // 查找年龄=40的人的[姓名,id]列表
        var nameIdList = personList.Where(p => p.Age == 40).Select(p => new { p.Name, p.Id }).ToList();

        //三、数据总量和存在性
        bool b1 = personList.Any();//列表只要数据存在元素返回true
        bool b2 = personList.Any((p) => p.Age < 50);//列表只要一个符合=>返回True (这个和下面这个是一样的功能)
        bool b3 = personList.Exists((p) => p.Age < 50);//列表只要一个符合=>返回True
        bool b4 = personList.TrueForAll((p) => p.Age < 50);//列表都符合该条件=>返回True(和IEnumerable<T>的All方法是一个意思)
        int count = personList.Count((p) => p.Age >= 20);//返回满足条件的条数

        //四、排序和颠倒
        var orderList1 = personList.OrderBy(p => p.Score).ThenBy(p => p.Age);//先按照Score升序排序,再按照Age升序排序
        var orderList2 = personList.OrderByDescending(p => p.Score).ThenBy(p => p.Age);//先按照Score降序排序,再按照Age升序排序
        personList.Reverse();//元素顺序反转,不需要赋值,会更改原始列表

        //五、聚合函数
        // 1. 计算所有人的年龄总和
        var sum = personList.Sum(p=>p.Age);
        // 2. 计算所有人的平均年龄
        var avg = personList.Select(p => p.Age).DefaultIfEmpty().Average();
        // 3. 获取所有人中的最大年龄
        var max = personList.Select(p => p.Age).DefaultIfEmpty().Max();
        // 4. 获取所有人中的最小年龄
        var min = personList.Select(p => p.Age).DefaultIfEmpty().Min();
        // 5. 获取年龄最大的人(返回的是整个实体对象)
        var max_entity = personList.MaxBy(p => p.Age);
        // 6. 获取年龄最小的人(返回的是整个实体对象)
        var min_entity = personList.MinBy(p => p.Age);


        //六、列表添加操作
        //(1)两个列表相加,返回给第三个列表(不修改personList列表)
        var personList3 = personList.Concat(personList2);

        //(2)往personlist里面加入personlist2列表(会直接修改personlist列表)
        personList.AddRange(personList2);

        //(3)往personlist里面加入person实体
        Person person = new Person { Id = 5, Name = "小加入", Age = 16, Score = 92 };
        personList.Add(person);

        //七、实体提取成字典
        //要求:姓名为键 分数为值,存进字典里
        Dictionary<string, int> dict = personList.ToDictionary(x => x.Name, x => x.Score);

        //八、列表修改操作(都会修改原始列表)
        personList.ForEach(x => x.Score = x.Score - 1);//列表所有Score-1
        personList.Where(x => x.Score >= 80).ToList().ForEach(x => x.Score = x.Score - 1); //列表成绩大于80的Score-1
        personList.Where(x => x.Score >= 80).ToList().ForEach(x =>
        {
            x.Score -= 1;//列表成绩大于80的Score-1
            x.Age += 1;//列表成绩大于80的Age+1
            x.Name = "姓名:" + x.Name;//列表成绩大于80的姓名前加上字符串"姓名:" 
        });

        //九、列表删除操作
        //(1)根据条件删除第一个符合条件的元素
        personList.Remove(personList.FirstOrDefault(x => x.Age == 40));//删除第一个年龄为40的人

        //(2)根据条件删除所有符合条件的元素
        personList.RemoveAll(x => x.Score < 60); // 删除所有分数小于60的人

        //(3)根据索引删除元素
        personList.RemoveAt(1); // 删除索引为1的元素

        //十、常数列表
        var allnumberlisit = Enumerable.Range(1, 80).ToList();//生成一个List<int>,其中从1开始,长度是80 ,即生成的是:1,2,3,…,80
    }
}
class Person
{
    public int Id;
    public string Name;
    public int Age;
    public int Score;

    public override string ToString()
    {
        return Id + "," + Name + "," + Age + "," + Score;
    }
}

【注意】ForEach是没有返回值的,可传入委托(方法)

//无返回值
tableList = tableList.ForEach(x => x.SalePrice =(x.ConsumeHour/8)* x.SalePrice);//错误
tableList.ForEach(x => x.SalePrice =(x.ConsumeHour/8)* x.SalePrice);//正确

//可传入委托
List<SchoolData> list = GetDatas();//模拟查库
list.ForEach(UpdateCreator);
private static void UpdateCreator(SchoolData input) 
{
    input.Creator = "后端测试";
}  

【注意】remove方法:如果没有该实体,也不会报错。(比如list_int=1,2,3 remove 4 也不会报错)

【注意】Select方法,第二入参定位(x代表被遍历的每个元素,index代表索引位置)

public class Data
{
    public int intdata { get; set; }

    public string strdata { get; set; }
}
static void Main(string[] args)
{

    List<Data> data = new List<Data>()
    {
            new Data { intdata = 1, strdata = "Data 1" },
            new Data { intdata = 2, strdata = "Data 2" },
            new Data { intdata = 3, strdata = "Data 3" },
            new Data { intdata = 4, strdata = "Data 4" },//需筛选其位置
            new Data { intdata = 5, strdata = "Data 5" },
            new Data { intdata = 6, strdata = "Data 6" },//需筛选其位置
            new Data { intdata = 7, strdata = "Data 7" },
            new Data { intdata = 8, strdata = "Data 8" },
            new Data { intdata = 9, strdata = "Data 9" },
            new Data { intdata = 10, strdata = "Data 10" }
    };

    var r1 = data.Where(x => x.intdata == 4 || x.intdata == 6)
                .Select((x, index) => index + 1);//错误写法,只能得出筛选后的索引,即1,2

    var r2 = data.Select((list, index) => new { list, index })  // 保留原始索引
                    .Where(x => x.list.intdata == 4 || x.list.intdata == 6)
                    .Select(x => x.index + 1);  // 使用原始索引,最后结果:4,6
}

【注意】Average、Max、Min方法:加上 DefaultIfEmpty 确保代码健壮性

var avg1 = personList.Average(p => p.Age); //这种不好,数据为空会报错
var avg2 = personList.Select(p => p.Age).Average(); //这种不好,数据为空会报错
var avg3 = personList.Select(p => p.Age).DefaultIfEmpty().Average();//推荐!DefaultIfEmpty 还可以入参一个默认值,例如DefaultIfEmpty(-99)

二、复杂排序

访问链接

三、集合计算(交并差+父集子集)

这里只提供简单版本,复杂的实体版本请看:C#高级:利用LINQ进行实体列表的集合运算(交集、差集、左外连接)_c# 差集-优快云博客

1.交并差集

// 旧数据(后端存储)
List<string> alist = new List<string> { "a", "b", "c", "d" };//列表a

// 新数据(前端请求)
List<string> blist = new List<string> { "c", "d", "e", "f" };//列表b

// 获取b有且a没有的数据(增加操作,差集)
List<string> toAdd = blist.Except(alist).ToList();//e f

// 获取a有且b没有的数据(删除操作,差集)
List<string> toRemove = alist.Except(blist).ToList();//a b

// 获取 a 和 b 中都共同存在的数据(此处alist blist位置可以互换,交集)
List<string> common = alist.Intersect(blist).ToList();//c d

// 获取 a 和 b 各自的全部元素的总和(并集)
List<string> union = alist.Union(blist).ToList();//a b c d e f

// 获取 a + b 但不包含共有部分的数据(对称差集)
List<string> unique = union.Except(common).ToList();//a b e f

2.父集子集

// 初始数据
List<string> list1 = new List<string> { "apple", "banana", "cherry" };
List<string> list2 = new List<string> { "banana", "cherry" };
List<string> list3 = new List<string> { "apple", "banana", "cherry" };
List<string> list4 = new List<string> { "apple", "banana", "cherry","mike" };

// 01 子集
// 判断list2/list3/list4是否是list1的子集
bool isSubset1 = list2.All(list1.Contains);//true
bool isSubset2 = list3.All(list1.Contains);//true
bool isSubset3 = list4.All(list1.Contains);//false

// 02 真子集
// 判断list2/list3/list4是否是list1的真子集
bool isProperSubset1 = list2.All(list1.Contains) && list1.Count > list2.Count;//true
bool isProperSubset2 = list3.All(list1.Contains) && list1.Count > list3.Count;//false
bool isProperSubset3 = list4.All(list1.Contains) && list1.Count > list4.Count;//false

四、类型的批量转化(字段映射)

1、非引用类型

 下面的例子中,用select关键字也是可以的

//1.List<double> => List<string>
datalist.ConvertAll(x => x.ToString()).ToArray()

//2.string  =>  List<char>  =>  List<string>
var list = "123456".ToList().ConvertAll(x=>x.ToString()); //思路一
var list2 = "123456".ToList().Select(x=>x.ToString()); //思路二

2、引用类型

//1.同一实体  列表字段映射(list1=>list2)
List<Student> list2 = new List<Student>(list1);


//2.不同实体
//(1)用一些Nuget包封装的Map方法映射字段【本质是(2)】
//(2)利用自主封装的反射映射方法
//(3)遍历
var destinctlist = new List<TableName>();
foreach (var item in result)
{
    var des = new TableName();
    des.a=item.a;
    des.b=item.b;
    des.c=item.c;
    des.d=item.d;
    des.e=item.e;
    destinctlist.Add(des)
}

//(4)Linq的Select(仅在不涉及复杂逻辑代码时推荐)
var distinctList = result.Select(item => new TableName
{
    a = item.a,
    b = item.b,
    c = item.c,
    d = item.d,
    e = item.e
}).ToList();

3、List<object>转换

【Oftype方法】

//OfType<Student>() 可以安全地筛选出所有你指定的对象(忽略其他对象)不会轻易报错,而调用 Cast<T>(),若其中一个元素转化失败都会报错。
List<object> objects = new List<object> { new Student(), new Student(), new object(),new Teacher(),"666",123,true };
List<Student> students = objects.OfType<Student>().ToList();//输出两个 Student 实例

【as方法】

var list1 = new List<City>() { new City { CityCode = 1, CityName = "测试" } };
var list2 = new object();
list2 = list1;
var list3 = list2 as List<City>;

五、字段提取和字段子项提取

【Demo演示】

        Select的意义是提取字段扔进集合,SlectMany的意义是提取字段的子项扔进集合

class Student
{
    public string Name { get; set; }
    public List<string> Courses { get; set; }
}
 
class Program
{
    static void Main()
    {
        var students = new List<Student>
        {
            new Student { Name = "Alice", Courses = new List<string> { "Math", "Science" } },
            new Student { Name = "Bob", Courses = new List<string> { "English", "History" } },
            new Student { Name = "Charlie", Courses = new List<string> { "Art", "Music" } }
        };
 
        //select的意义是提取字段扔进集合,slectmany的意义是提取字段的子项扔进集合
        List<string> name_list = students.Select(x => x.Name).ToList();
        List<List<string>> course_list_list = students.Select(x => x.Courses).ToList();
        List<string> course_list = students.SelectMany(x => x.Courses).ToList();
        List<char> name_item_list = students.SelectMany(x => x.Name).ToList();
        
    }
}

再直白点,一般情况下,select提取内容是T,SelectMany提取的内容是List<T>

将结果映射到一个实体中:

datalist.Select(x=> new Entity { StreeName=x.Town,PointName=x.PositionName}).ToList();

六、用Any和All做元素规则判断

string a = "0";
string b = "abc";
string c = null;
List<string> list = new List<string>() { a,b,c};

// 01 All关键字:【全部都满足】该方法才返回True
var result1 = list.All(string.IsNullOrEmpty);// 输出False

// 02 Any关键字:【其中一个满足】该方法就返回True
var result2 = list.Any(string.IsNullOrEmpty);// 输出True

// 03 传入的变量,本质是一个【入参类型为List的子项类型,出参为bool】的委托
Func<string, bool> method = (input) => input?.StartsWith("ab") ?? false;
var result3 = list.Any(method);//输出True
bool areAllNullOrEmpty = new[] { a, b, c }.All(string.IsNullOrEmpty);//如果全部都是(null或空字符串),输出True

八、重复元素 & 去重

//【普通类型】筛选出重复数据
//假如我有一个list<string> 里面有很多数字,用一句代码筛选出重复出现过的数字
var duplicates = list.GroupBy(x => x)
                     .Where(g => g.Count() > 1) // 没有重复出现过的数字:g.Count() == 1
                     .Select(g => g.Key)
                     .ToList();
/*------------------------------------------------------------------*/


//【普通类型】去重
var uniqueList = list.Distinct().ToList();

//【引用类型】去重
//1.使用 GroupBy 去重
var uniqueData = schoolDataList
            .GroupBy(d => new { d.ID, d.Time })  // 按 ID 和 Time 分组
            .Select(g => g.First())  // 对每个组取第一个元素
            .ToList();  // 转换回 List

//2.使用 Distinct 去重【推荐】
var uniqueData = schoolDataList
    .DistinctBy(d => new { d.ID, d.Time })  // 根据 ID 和 Time 去重,默认对每个组取第一个元素
    .ToList();

九、列表的相互嵌套查询

【推荐】先查一列后筛查,效率高且简单

class Program
{
    // 学生类
    public class Student
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
    }

    // 老师类
    public class Teacher
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public int SID { get; set; }  // 关联的学生ID
    }

    static void Main(string[] args)
    {
        // 创建学生列表
        var students = new List<Student>
        {
            new Student { ID = 1, Name = "Alice", Age = 16 },
            new Student { ID = 2, Name = "Bob", Age = 14 },
            new Student { ID = 3, Name = "Charlie", Age = 18 },
            new Student { ID = 4, Name = "David", Age = 20 }
        };

        // 创建老师列表
        var teachers = new List<Teacher>
        {
            new Teacher { ID = 1, Name = "Mr. Smith", SID = 1 },
            new Teacher { ID = 2, Name = "Ms. Johnson", SID = 2 },
            new Teacher { ID = 3, Name = "Dr. Brown", SID = 3 },
            new Teacher { ID = 4, Name = "Mrs. White", SID = 4 }
        };

        //场景:筛选出学生Age>15的老师信息(推荐第一种做法)

        //01 先查SID再筛查【写法中等 性能好】【推荐】
        var studentSIDs = students.Where(s => s.Age > 15).Select(s => s.ID).ToList();
        var result1 = teachers.Where(t => studentSIDs.Contains(t.SID))
                              .ToList();

        //02 嵌套Any【写法简洁 性能差】
        var result2 = teachers.Where(t => students.Any(s => s.ID == t.SID && s.Age > 15))
                              .ToList();

        //03 内连接Join【写法较难 性能好】
        var result3 = teachers.Join(students,
                                    t => t.SID,  // Teacher 的 SID
                                    s => s.ID,   // Student 的 ID
                                    (t, s) => new { Teacher = t, Student = s }) // 通过匿名对象返回 Teacher 和 Student
                            .Where(ts => ts.Student.Age > 15) // 过滤出学生年龄大于 15 的教师
                            .Select(ts => ts.Teacher) // 只返回 Teacher 对象
                            .ToList();  // 转换为列表

        //04 转换为字典【写法中等 性能好】
        Dictionary<int, Student> studentDict = students.ToDictionary(s => s.ID); // 转换为字典
        var result4 = teachers.Where(t => studentDict.ContainsKey(t.SID) && studentDict[t.SID].Age > 15)
                              .ToList();

        //05 查询句法【写法中等 性能好】
        var result5 = (from t in teachers
                       join s in students on t.SID equals s.ID
                       where s.Age > 15
                       select t).ToList();
        Console.ReadLine();
    }
}

十、列表拆分(分页)

// 创建学生列表
var students = new List<Student>
{
    new Student { ID = 1, Name = "Alice", Age = 16 },
    new Student { ID = 2, Name = "Bob", Age = 14 },
    new Student { ID = 3, Name = "Charlie", Age = 18 },
    new Student { ID = 4, Name = "David", Age = 20 }
};

List<Student[]> chunks = students.Chunk(2).ToList();//每两个实体为一组(列表4条数据,共分得两组,即chunks的长度为2)
Student[] firstGroup = chunks[0];//Alice  Bob

更多扩展方法

C#高级:常用的扩展方法大全-优快云博客

本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值