C#之LINQ

文章目錄

  • 前言
    • LINQ
  • 一、LINQ1
  • 一、LINQ2
  • 一、LINQ3
  • Where方法:每一項數據都會進過predicate的測試,如果針對一個元素,predicate執行的返回值為true,那么這個元素就會放到返回值中。
  • 獲取一條數據(是否帶參數的兩種寫法):
    • C# LINQ 查詢方法詳解:Single, SingleOrDefault, First, FirstOrDefault
    • 方法對比表
    • 詳細解釋與示例
      • 1. Single
      • 2. SingleOrDefault
      • 3. First
      • 4. FirstOrDefault
    • 性能考慮
    • 默認值說明
    • 最佳實踐建議
    • 總結對比圖
  • 排序:
    • C# LINQ 排序方法詳解:OrderBy 與 OrderByDescending
    • 基本概念
    • 基本語法
    • 示例解釋:`list.OrderBy(e => e.Age)`
      • 執行過程:
      • 排序結果:
    • 完整排序示例
      • 1. 單屬性排序
      • 2. 多級排序(ThenBy/ThenByDescending)
      • 3. 自定義排序邏輯
    • C# 特殊排序場景詳解:簡單類型、末位字符與隨機排序
    • 一、簡單類型排序(不使用 Lambda 表達式)
      • 1. 基本排序方法
      • 2. C# 11+ 的簡化語法
      • 3. 字符串集合排序
    • 二、特殊案例:按最后一個字符排序
      • 1. 基本實現
      • 2. 處理空字符串和單字符
      • 3. 多級排序(先按長度,再按末字符)
    • 三、隨機排序(使用 Guid 或隨機數)
      • 1. 使用 Guid 隨機排序
  • 限制結果集,獲取部分數據:
    • C# LINQ 分頁操作詳解:Skip 與 Take
    • 基本概念
    • 基本語法
    • 方法詳解
      • 1. Skip(n)
      • 2. Take(n)
    • 組合使用:分頁實現
      • 基本分頁公式
      • 完整分頁示例
      • 分頁輔助方法
  • 集合函數:
    • C# LINQ 聚合方法與鏈式調用詳解
    • LINQ 聚合方法概述
    • 鏈式調用原理
      • 鏈式調用示例
    • 代碼解析:`list.Where(e=>e.Age>30).Min(e=>e.Age);`
      • 執行步驟
      • 等效傳統代碼
      • 注意事項
    • 其他聚合方法鏈式調用示例
      • 1. 計算平均值
      • 2. 求和統計
      • 3. 計數統計
      • 4. 多級聚合
    • 鏈式調用的高級應用
      • 1. 條件聚合
      • 2. 組合使用
      • 3. 空值處理技巧
  • 分組:
    • C# LINQ GroupBy 分組方法詳解
    • GroupBy 方法核心概念
      • 方法簽名
      • 關鍵特性
    • IGrouping 接口解析
      • 核心特性
    • 基本用法示例
      • 1. 簡單分組
      • 2. 分組后聚合計算
    • 高級分組技巧
      • 1. 復合鍵分組
      • 2. 分組后元素轉換
      • 3. 自定義結果選擇器
    • IGrouping 的實際應用
      • 1. 直接訪問分組鍵
      • 2. 分組嵌套處理
      • 3. 轉換為字典
    • 性能注意事項
    • 投影:
  • C# LINQ 投影操作詳解
    • 投影的本質
  • C# LINQ 投影操作詳解
    • 投影的本質
    • 核心方法:Select()
      • 方法簽名
    • 投影的基本用法
      • 1. 提取屬性值
      • 2. 創建新對象
      • 3. 轉換類型
    • 高級投影技巧
      • 1. 帶索引的投影
      • 2. 嵌套投影
      • 3. 條件投影
      • 4. 計算字段投影
    • 實際應用場景
      • 1. 數據轉換(Entity → DTO)
      • 2. 數據簡化
      • 3. 計算字段
      • 4. 組合數據
    • 性能考慮
    • 與 SelectMany() 的區別
    • 最佳實踐
    • 集合轉換:


前言

LINQ

一、LINQ1

委托->lambda->LINQ
1、委托是可以指向方法的類型,調用委托變量時執行的就是變量指向方法。

在 C# 中,??委托(Delegate)?? 是一種類型安全的函數指針,它允許將方法作為參數傳遞、存儲或動態調用。委托是事件(Event)和回調機制的基礎,實現了??松耦合??的設計模式。
核心概念??
1.類型安全的方法引用??

委托定義了方法的簽名(參數類型和返回類型),只能綁定匹配簽名的方法。

2.類似接口??

委托類似于只包含一個方法的接口,但更輕量且直接。

3.多播能力??

一個委托實例可綁定多個方法(+= 添加),調用時按順序執行所有方法。

委托的聲明與使用?

  1. 定義委托類型
// 聲明一個委托類型,指定方法簽名
public delegate void MyDelegate(string message);
  1. 綁定方法
// 目標方法(簽名必須匹配)
public void ShowMessage(string msg)
{Console.WriteLine($"Message: {msg}");
}// 實例化委托并綁定方法
MyDelegate del = new MyDelegate(ShowMessage);
  1. 調用委托
del("Hello, Delegate!"); 
// 輸出:Message: Hello, Delegate!

實例

class Program
{static void Main(string[] args){D1 d = F1;d();d = F2;d();}static void F1(){Console.WriteLine("我是F1");}static void F2(){Console.WriteLine("我是F2");}
}
delegate void D1();

結果
在這里插入圖片描述

2、.NET中定義了泛型委托Action(無返回值)和Func(有返回值),所以一般不用自定義委托類型

內置泛型委托??
C# 提供兩種常用泛型委托,無需自定義:

1.??Action??

無返回值的方法(支持 0~16 個參數)。

public delegate void Action();                     // 無參數
public delegate void Action<in T>(T obj);          // 1個參數
public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2); // 2個參數
// ... 最多支持16個參數 (Action<T1,...,T16>)
Action<string> actionDel = ShowMessage; // void 方法

2.??Func??
有返回值的方法(最后一個泛型參數是返回類型)。

public delegate TResult Func<out TResult>();                     // 無參數,有返回值
public delegate TResult Func<in T, out TResult>(T arg);          // 1輸入+1輸出
public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2); // 2輸入+1輸出
// ... 最多16輸入+1輸出 (Func<T1,...,T16,TResult>)
Func<int, int, int> add = (a, b) => a + b; 
int result = add(3, 5); // 返回 8

委托變量不僅可以指向普通方法,還可以指向匿名方法。

Func<int, int, string> f1 = delegate (int i1, int i2)
{return $"{i1}+{i2}={i1 + i2}";
};

匿名方法可以寫成lambda表達式,可以省略參數數據類型,因為編譯根據委托類型推斷出參數類型,用=>引出方法體

        Func<int, int, string> f2 = (i1, i2) =>{return $"{i1}+{i2}={i1 + i2}";};

lambda表達式

(輸入參數) => 表達式或語句塊
場景Lambda 表達式等效傳統寫法
無參數() => Console.WriteLine("Hi")void F() { Console.WriteLine("Hi"); }
單參數x => x * xint F(int x) { return x * x; }
多參數(a, b) => a + bint F(int a, int b) { return a + b; }
語句塊s => { Console.WriteLine(s); return s.Length; }int F(string s) { Console.WriteLine(s); return s.Length; }

一、LINQ2

揭秘LINQ方法的背后
LINQ中提供了很多集合擴展方法,配合lambda能簡化數據處理。

int[] nums = { 11, 1, 24, 5, 6, 98, 60 };
// Where方法會遍歷集合中的每一個元素,對于每一個元素
// 都調用a=> a>10這個表達式判斷下一個是否為true
// 如果為true,則把這個放到返回的集合中
IEnumerable<int> result = nums.Where(a => a > 10);
foreach (int i in result)
{Console.WriteLine(i);
}

下面手動實現Where功能

int[] nums = { 11, 1, 24, 5, 6, 98, 60 };
//// Where方法會遍歷集合中的每一個元素,對于每一個元素
//// 都調用a=> a>10這個表達式判斷下一個是否為true
//// 如果為true,則把這個放到返回的集合中
//IEnumerable<int> result = nums.Where(a => a > 10);
IEnumerable<int> result = MyWhere(nums,a=>a>10);
foreach (int i in result)
{Console.WriteLine(i);
}IEnumerable<int> MyWhere(IEnumerable<int> items,Func<int,bool> f)
{List<int> result = new List<int>();foreach (int item in items){if (f(item))result.Add(item);}return result;
}

使用yield實現Where功能

int[] nums = { 11, 1, 24, 5, 6, 98, 60 };
//// Where方法會遍歷集合中的每一個元素,對于每一個元素
//// 都調用a=> a>10這個表達式判斷下一個是否為true
//// 如果為true,則把這個放到返回的集合中
//IEnumerable<int> result = nums.Where(a => a > 10);
IEnumerable<int> result = MyWhere1(nums,a=>a>10);
foreach (int i in result)
{Console.WriteLine(i);
}IEnumerable<int> MyWhere1(IEnumerable<int> items, Func<int, bool> f)
{List<int> result = new List<int>();foreach (int item in items){if (f(item))yield return item;}
}

一、LINQ3

LINQ常用擴展方法

(補充)擴展方法
#C# 擴展方法深度解析

一、本質與原理

  1. 核心概念
    擴展方法是一種編譯時語法糖,它允許開發者在不修改原始類型、不創建派生類的情況下,為現有類型"添加"新方法。其本質是靜態方法,但通過編譯器魔法實現了實例方法調用語法。

  2. 實現機制

// 定義擴展方法
public static class StringExtensions {public static bool IsValidEmail(this string input) => Regex.IsMatch(input, @"^[^@\s]+@[^@\s]+\.[^@\s]+$");
}// 調用代碼
var isValid = "test@example.com".IsValidEmail();// 編譯器轉換后的實際代碼
var isValid = StringExtensions.IsValidEmail("test@example.com");
  1. 關鍵特性
  • 靜態偽裝:靜態方法偽裝成實例方法
  • 非侵入性:不修改原始類型代碼
  • 編譯時解析:在編譯階段確定方法綁定
  • 命名空間控制:需導入擴展方法所在命名空間

二、技術實現詳解

  1. 三大必要條件
public static class Extensions // 條件1:靜態類
{// 條件2:靜態方法 + 條件3:this修飾首參數public static string Reverse(this string value){char[] chars = value.ToCharArray();Array.Reverse(chars);return new string(chars);}
}
  1. 參數規則
  • 首個參數:必須使用 this 修飾,指定目標類型
  • 附加參數:可添加多個常規參數
public static string Wrap(this string text, string wrapper)=> $"{wrapper}{text}{wrapper}";// 使用
"Hello".Wrap("**"); // 輸出:**Hello**
  1. 方法重載
// 重載1:默認包裝符
public static string Wrap(this string text) => Wrap(text, "[]");// 重載2:自定義包裝符
public static string Wrap(this string text, string wrapper) => $"{wrapper}{text}{wrapper}";

三、高級應用場景

  1. 接口擴展
public static void Log<T>(this IEnumerable<T> collection)
{foreach (var item in collection)Console.WriteLine(item);
}// 所有集合類型通用
new List<int>{1,2,3}.Log();
new int[]{4,5,6}.Log();
  1. 鏈式調用 (Fluent API)
public static StringBuilder AppendFormattedLine(this StringBuilder sb,string format,params object[] args)
{sb.AppendFormat(format, args).AppendLine();return sb; // 返回自身實現鏈式調用
}// 使用
var sb = new StringBuilder().AppendFormattedLine("Date: {0}", DateTime.Now).AppendFormattedLine("User: {0}", "Alice");
  1. 空值處理模式
public static TResult SafeGet<T, TResult>(this T obj, Func<T, TResult> selector,TResult defaultValue = default)
{return obj != null ? selector(obj) : defaultValue;
}// 安全訪問嵌套屬性
var city = person?.Address?.City; // 傳統方式
var city = person.SafeGet(p => p.Address.City); // 擴展方法方式

四、LINQ風格通用擴展方法

  1. 完整實現示例
public static class EnumerableExtensions
{// 通用過濾 (支持所有IEnumerable<T>)public static IEnumerable<T> WhereEx<T>(this IEnumerable<T> source,Func<T, bool> predicate){foreach (var item in source)if (predicate(item)) yield return item;}// 通用轉換public static IEnumerable<TResult> SelectEx<TSource, TResult>(this IEnumerable<TSource> source,Func<TSource, TResult> selector){foreach (var item in source)yield return selector(item);}// 字典鍵過濾專用public static IEnumerable<TKey> KeysWhere<TKey, TValue>(this IDictionary<TKey, TValue> source,Func<TKey, bool> predicate){foreach (var key in source.Keys)if (predicate(key))yield return key;}
}
  1. 多類型兼容使用
// List使用
var numbers = new List<int> {1, 2, 3, 4};
var evens = numbers.WhereEx(n => n % 2 == 0);// 數組使用
string[] fruits = {"Apple", "Banana"};
var aFruits = fruits.WhereEx(f => f.StartsWith("A"));// 字典使用
var dict = new Dictionary<int, string> {{1, "A"}, {2, "B"}};
var keys = dict.KeysWhere(k => k > 1); // [2]

Where方法:每一項數據都會進過predicate的測試,如果針對一個元素,predicate執行的返回值為true,那么這個元素就會放到返回值中。

Where參數是一個lambda表達式格式的匿名方法,方法的參數e表示當前判斷的元素對象。參數的名字不一定非要叫e,不過一般lambda表達式中的變量名長度都不長。
Count方法:獲取數據條數
Any方法:是否至少有一條數據

List<Employee> list = new List<Employee>();
list.Add(new Employee { Id = 1, Name = "jerry", Age = 28, Gender = true, Salary = 5000 });
list.Add(new Employee { Id = 2, Name = "jim", Age = 33, Gender = true, Salary = 3000 });
list.Add(new Employee { Id = 3, Name = "lily", Age = 35, Gender = false, Salary = 9000 });
list.Add(new Employee { Id = 4, Name = "lucy", Age = 16, Gender = false, Salary = 2000 });
list.Add(new Employee { Id = 5, Name = "kimi", Age = 25, Gender = true, Salary = 1000 });
list.Add(new Employee { Id = 6, Name = "nancy", Age = 35, Gender = false, Salary = 8000 });
list.Add(new Employee { Id = 7, Name = "zack", Age = 35, Gender = true, Salary = 8500 });
list.Add(new Employee { Id = 8, Name = "jack", Age = 33, Gender = true, Salary = 8000 });
IEnumerable<Employee> items1 = list.Where(e => e.Age > 30);// 返回符合條件的IEnumerable集合
int items2 = list.Count();// 無條件返回總條數
int items3 = list.Count(e => e.Age > 30);// 返回符合條件的,數量
int items4 = list.Count(e => e.Age > 30 && e.Salary>500);// 返回符合條件的,數量
bool items5 = list.Any();// 有一條數據就返回true否則返回false
bool items6 = list.Any(e => e.Age > 30);// 有一條數據就返回true否則返回false,找到一條符合條件的數據就返回true,且不會繼續尋找后面的數據,否則返回falseforeach (Employee item in items1)
{Console.WriteLine(item.Name);
}

獲取一條數據(是否帶參數的兩種寫法):

Single:有且只有一條滿足要求的數據;
SingleOrDefault:最多只有一條滿足要求的數據;
First:至少有一條,返回第一條;
FirstOrDefault:返回第一條或者默認值;

C# LINQ 查詢方法詳解:Single, SingleOrDefault, First, FirstOrDefault

在 C# 的 LINQ 查詢中,Single, SingleOrDefault, FirstFirstOrDefault 是常用的元素檢索方法,它們有不同的行為和使用場景。下面我將詳細解釋它們的區別和使用方法。

方法對比表

方法返回值條件無匹配時行為多個匹配時行為使用場景
Single有且只有一條滿足要求的數據拋出 InvalidOperationException拋出 InvalidOperationException確保只有唯一匹配項時
SingleOrDefault最多只有一條滿足要求的數據返回默認值(如 null 或 0)拋出 InvalidOperationException期望0或1個匹配項時
First至少有一條,返回第一條拋出 InvalidOperationException返回第一個匹配項需要第一個匹配項且確保存在時
FirstOrDefault返回第一條或者默認值返回默認值(如 null 或 0)返回第一個匹配項需要第一個匹配項或處理空結果時

詳細解釋與示例

1. Single

  • 行為:要求序列中有且只有一個元素滿足條件
  • 異常情況
    • 如果沒有匹配項 → 拋出 InvalidOperationException
    • 如果有多個匹配項 → 拋出 InvalidOperationException
  • 使用場景:當你確定只有一個匹配項時
  • 示例
// 查找唯一ID為3的員工
var employee = list.Single(e => e.Id == 3);
Console.WriteLine(employee.Name); // 輸出: lily// 以下情況會拋出異常:
// var invalid1 = list.Single(e => e.Age > 40); // 無匹配項
// var invalid2 = list.Single(e => e.Age == 35); // 多個匹配項

2. SingleOrDefault

  • 行為:要求序列中最多只有一個元素滿足條件
  • 異常情況
    • 如果沒有匹配項 → 返回類型的默認值(如 null, 0 等)
    • 如果有多個匹配項 → 拋出 InvalidOperationException
  • 使用場景:當你期望0或1個匹配項時
  • 示例
// 查找唯一ID為10的員工(不存在)
var employee1 = list.SingleOrDefault(e => e.Id == 10);
Console.WriteLine(employee1?.Name ?? "未找到"); // 輸出: 未找到// 查找唯一ID為3的員工(存在)
var employee2 = list.SingleOrDefault(e => e.Id == 3);
Console.WriteLine(employee2.Name); // 輸出: lily// 以下情況會拋出異常:
// var invalid = list.SingleOrDefault(e => e.Age == 35); // 多個匹配項

3. First

  • 行為:返回序列中第一個滿足條件的元素
  • 異常情況
    • 如果沒有匹配項 → 拋出 InvalidOperationException
    • 如果有多個匹配項 → 返回第一個匹配項
  • 使用場景:當你需要第一個匹配項確保存在
  • 示例
// 查找第一個年齡大于30的員工
var employee = list.First(e => e.Age > 30);
Console.WriteLine(employee.Name); // 輸出: jim// 以下情況會拋出異常:
// var invalid = list.First(e => e.Age > 40); // 無匹配項

4. FirstOrDefault

  • 行為:返回序列中第一個滿足條件的元素,或默認值
  • 異常情況
    • 如果沒有匹配項 → 返回類型的默認值(如 null, 0 等)
    • 如果有多個匹配項 → 返回第一個匹配項
  • 使用場景:當你需要第一個匹配項或處理空結果
  • 示例
// 查找第一個年齡大于40的員工(不存在)
var employee1 = list.FirstOrDefault(e => e.Age > 40);
Console.WriteLine(employee1?.Name ?? "未找到符合條件的員工"); // 輸出: 未找到符合條件的員工// 查找第一個年齡為35的員工
var employee2 = list.FirstOrDefault(e => e.Age == 35);
Console.WriteLine(employee2.Name); // 輸出: lily

性能考慮

  • First/FirstOrDefault 通常比 Single/SingleOrDefault 性能更好,因為它們找到第一個匹配項就返回
  • Single/SingleOrDefault 需要遍歷整個序列以確保只有一個匹配項

默認值說明

對于引用類型:

  • 默認值為 null
  • 使用前應檢查是否為 null

對于值類型:

  • 默認值為該類型的默認值(如 int 為 0,bool 為 false)
  • 使用前應檢查是否等于默認值

最佳實踐建議

  1. 優先使用 FirstOrDefault

    • 大多數情況下是最安全的選擇
    • 避免異常處理,代碼更簡潔
  2. 謹慎使用 Single

    • 僅在確保只有一個匹配項時使用
    • 數據庫主鍵查詢是典型場景
  3. 避免在可能多個匹配項時使用 SingleOrDefault

    • 多個匹配項會拋出異常
    • 使用前應確認數據唯一性
  4. 處理默認值

var result = list.FirstOrDefault();
if (result != null) // 對于引用類型
{// 處理結果
}var valueResult = intList.FirstOrDefault();
if (valueResult != default) // 對于值類型
{// 處理結果
}

總結對比圖

       唯一性要求         存在性要求↓               ↓
Single:     有且只有一條滿足要求的數據
SingleOrDefault: 最多只有一條滿足要求的數據
First:              至少有一條,返回第一條
FirstOrDefault:         返回第一條或者默認值

根據你的具體需求選擇合適的方法:

  • 需要唯一結果 → SingleSingleOrDefault
  • 需要第一個結果 → FirstFirstOrDefault
  • 不確定是否存在結果 → ...OrDefault 版本
  • 確保結果存在 → 不帶 OrDefault 的版本

排序:

OrderBy() 對數據正序排序;
OrderByDescending()倒序排序;
list.OrderBy(e=>e.Age);
對于簡單類型排序,也許不用lambda表達式。特殊案例:按照最后一個字符排序,用Guid或者隨機數進行隨機排序。

C# LINQ 排序方法詳解:OrderBy 與 OrderByDescending

在 C# 的 LINQ 查詢中,OrderBy()OrderByDescending() 是用于對數據進行排序的核心方法。下面我將詳細解釋它們的用法和區別。

基本概念

方法描述排序方向
OrderBy()對序列元素進行升序排序從小到大 (A→Z, 1→9)
OrderByDescending()對序列元素進行降序排序從大到小 (Z→A, 9→1)

基本語法

// 正序排序
IEnumerable<TSource> sortedAsc = source.OrderBy(e => e.Property);// 倒序排序
IEnumerable<TSource> sortedDesc = source.OrderByDescending(e => e.Property);

示例解釋:list.OrderBy(e => e.Age)

// 使用 OrderBy 按年齡正序排序
var sortedByAge = list.OrderBy(e => e.Age);

執行過程:

  1. 遍歷 list 中的所有員工
  2. 提取每個員工的 Age 屬性值作為排序鍵
  3. 按照年齡從小到大排序
  4. 返回排序后的新序列(原始列表不會被修改)

排序結果:

假設原始列表年齡為:[28, 33, 35, 16, 25, 35, 35, 33]
排序后變為:[16, 25, 28, 33, 33, 35, 35, 35]

完整排序示例

1. 單屬性排序

// 按年齡正序排序
var byAgeAsc = list.OrderBy(e => e.Age);// 按工資倒序排序
var bySalaryDesc = list.OrderByDescending(e => e.Salary);

2. 多級排序(ThenBy/ThenByDescending)

// 先按性別正序,再按年齡倒序
var multiSort = list.OrderBy(e => e.Gender)        // 先按性別排序(false在前,true在后).ThenByDescending(e => e.Age);  // 再按年齡降序// 先按年齡倒序,再按工資正序
var multiSort2 = list.OrderByDescending(e => e.Age).ThenBy(e => e.Salary);

3. 自定義排序邏輯

// 按姓名長度排序
var byNameLength = list.OrderBy(e => e.Name.Length);// 按工資范圍分組排序
var bySalaryRange = list.OrderBy(e => 
{if (e.Salary < 3000) return 1;    // 低薪組if (e.Salary < 6000) return 2;    // 中薪組return 3;                         // 高薪組
});

C# 特殊排序場景詳解:簡單類型、末位字符與隨機排序

在 C# 中,雖然 Lambda 表達式是 LINQ 排序的常見方式,但在某些特殊場景下,我們可以使用更簡潔或更靈活的方法進行排序。下面我將詳細解釋這些特殊排序場景的實現方式。

一、簡單類型排序(不使用 Lambda 表達式)

1. 基本排序方法

List<int> numbers = new List<int> { 5, 2, 8, 1, 9 };// 升序排序(不使用 Lambda)
var sortedAsc = numbers.OrderBy(n => n); // 傳統方式
var simpleAsc = numbers.Order();         // C# 11+ 簡化方式// 降序排序(不使用 Lambda)
var sortedDesc = numbers.OrderByDescending(n => n); // 傳統方式
var simpleDesc = numbers.OrderDescending();         // C# 11+ 簡化方式

2. C# 11+ 的簡化語法

在 C# 11 及以上版本中,對于簡單類型集合,可以直接使用:

// 升序排序
var sorted = numbers.Order();// 降序排序
var sortedDesc = numbers.OrderDescending();

3. 字符串集合排序

List<string> fruits = new List<string> { "Apple", "Banana", "Cherry", "Date" };// 按字母順序排序
var alphabetical = fruits.Order(); // ["Apple", "Banana", "Cherry", "Date"]// 按長度排序(仍需使用 Lambda)
var byLength = fruits.OrderBy(f => f.Length); // ["Date", "Apple", "Banana", "Cherry"]

二、特殊案例:按最后一個字符排序

1. 基本實現

List<string> words = new List<string> { "apple", "banana", "cherry", "date" };// 按最后一個字符升序排序
var byLastChar = words.OrderBy(w => w[1](@ref)); // ^1 表示最后一個字符// 結果: ["banana"(a), "apple"(e), "date"(e), "cherry"(y)]

2. 處理空字符串和單字符

public static char SafeLastChar(string s)
{return string.IsNullOrEmpty(s) ? '\0' : s[1](@ref);
}// 安全獲取最后一個字符并排序
var safeSorted = words.Where(w => !string.IsNullOrEmpty(w)).OrderBy(w => SafeLastChar(w));

3. 多級排序(先按長度,再按末字符)

var multiSort = words.OrderBy(w => w.Length).ThenBy(w => w[1](@ref));

三、隨機排序(使用 Guid 或隨機數)

1. 使用 Guid 隨機排序

// 使用 Guid 生成隨機排序鍵
var randomOrder = list.OrderBy(e => Guid.NewGuid()).ToList();// 原理:為每個元素分配唯一的隨機 Guid,然后排序

優點

  • 實現簡單,一行代碼
  • 分布均勻,隨機性好

缺點

  • 性能較差(生成 Guid 開銷大)
  • 不適用于大數據集

限制結果集,獲取部分數據:

Ship(n)跳過n條數據,Take(n) 獲取n條數據。
案例:獲取從第2條開始獲取3條數據 var orderedItems1 = list.Skip(2).Take(3);
Skip()、Take()也可以單獨使用。

C# LINQ 分頁操作詳解:Skip 與 Take

在 C# 的 LINQ 查詢中,Skip()Take() 是兩個用于數據分頁和子集選擇的核心方法。它們通常結合使用來實現高效的分頁功能。

基本概念

方法描述行為
Skip(n)跳過序列中的前 n 個元素返回剩余元素的序列
Take(n)從序列開頭獲取前 n 個元素返回包含前 n 個元素的序列

基本語法

// 跳過前 n 個元素
IEnumerable<T> skipped = source.Skip(n);// 獲取前 n 個元素
IEnumerable<T> taken = source.Take(n);// 組合使用(分頁)
IEnumerable<T> page = source.Skip(pageIndex * pageSize).Take(pageSize);

方法詳解

1. Skip(n)

  • 功能:跳過序列中的前 n 個元素
  • 參數:要跳過的元素數量
  • 返回值:包含源序列中跳過指定數量元素后的剩余元素
  • 邊界情況
    • 如果 n ≤ 0:返回整個序列
    • 如果 n ≥ 序列長度:返回空序列
List<int> numbers = new List<int> {1, 2, 3, 4, 5};// 跳過前 2 個元素
var skipped = numbers.Skip(2); // [3, 4, 5]// 跳過 0 個元素
var skipZero = numbers.Skip(0); // [1, 2, 3, 4, 5]// 跳過超過序列長度
var skipLarge = numbers.Skip(10); // 空序列

2. Take(n)

  • 功能:從序列開頭獲取指定數量的元素
  • 參數:要獲取的元素數量
  • 返回值:包含源序列前 n 個元素的序列
  • 邊界情況
    • 如果 n ≤ 0:返回空序列
    • 如果 n ≥ 序列長度:返回整個序列
List<int> numbers = new List<int> {1, 2, 3, 4, 5};// 獲取前 3 個元素
var taken = numbers.Take(3); // [1, 2, 3]// 獲取 0 個元素
var takeZero = numbers.Take(0); // 空序列// 獲取超過序列長度
var takeLarge = numbers.Take(10); // [1, 2, 3, 4, 5]

組合使用:分頁實現

基本分頁公式

int pageIndex = 2; // 第3頁(從0開始計數)
int pageSize = 3;  // 每頁3條var page = source.Skip(pageIndex * pageSize).Take(pageSize);

完整分頁示例

List<Employee> employees = GetEmployees(); // 假設有100名員工int pageSize = 10; // 每頁10條// 獲取第3頁數據(索引從0開始)
var page3 = employees.OrderBy(e => e.LastName) // 先排序.Skip(2 * pageSize)       // 跳過前20條.Take(pageSize);          // 取10條Console.WriteLine($"第3頁數據(共{page3.Count()}條):");
foreach (var emp in page3)
{Console.WriteLine($"{emp.LastName}, {emp.FirstName}");
}

分頁輔助方法

public static class PagingExtensions
{public static IEnumerable<T> Page<T>(this IEnumerable<T> source, int pageIndex, int pageSize){return source.Skip(pageIndex * pageSize).Take(pageSize);}public static IQueryable<T> Page<T>(this IQueryable<T> source, int pageIndex, int pageSize){return source.Skip(pageIndex * pageSize).Take(pageSize);}
}// 使用
var page = employees.Page(2, 10); // 獲取第3頁,每頁10條

集合函數:

Max()、Min()、Average()、Sum()、Count()。
LINQ中所有的擴展方法幾乎都是針對IEnumerable接口的,而幾乎所有能返回集合的都返回IEnumerable,所以是可以把幾乎所有方法“鏈式使用”的。list.Where(e=>e.Age>30).Min(e=>e.Age);

C# LINQ 聚合方法與鏈式調用詳解

LINQ 聚合方法概述

方法描述返回值類型空集合行為
Max()返回序列中的最大值數值類型拋出異常
Min()返回序列中的最小值數值類型拋出異常
Average()返回序列的平均值數值類型拋出異常
Sum()返回序列的總和數值類型返回0
Count()返回序列的元素數量int返回0

鏈式調用原理

LINQ 的核心設計理念是鏈式調用(Method Chaining),這得益于:

  1. 幾乎所有 LINQ 方法都是針對 IEnumerable<T> 接口的擴展方法
  2. 大多數方法返回 IEnumerable<T>IOrderedEnumerable<T>
  3. 每個方法操作前一個方法返回的結果集

鏈式調用示例

var result = employees.Where(e => e.Department == "IT")  // 返回 IEnumerable<Employee>.OrderBy(e => e.LastName)          // 返回 IOrderedEnumerable<Employee>.Select(e => new { e.Name, e.Salary }) // 返回 IEnumerable<匿名類型>.Take(10);                         // 返回 IEnumerable<匿名類型>

代碼解析:list.Where(e=>e.Age>30).Min(e=>e.Age);

執行步驟

  1. 過濾階段

    var filtered = list.Where(e => e.Age > 30);
    
    • 遍歷原始集合 list
    • 篩選出年齡大于30的元素
    • 返回 IEnumerable<Employee> 類型的結果集
  2. 聚合階段

    var minAge = filtered.Min(e => e.Age);
    
    • 遍歷過濾后的結果集 filtered
    • 提取每個元素的 Age 屬性
    • 找出這些年齡值中的最小值
    • 返回 int 類型的最小年齡值

等效傳統代碼

int minAge = int.MaxValue;
bool found = false;foreach (var employee in list)
{if (employee.Age > 30){found = true;if (employee.Age < minAge){minAge = employee.Age;}}
}if (!found)
{throw new InvalidOperationException("序列不包含任何元素");
}

注意事項

  1. 空集合處理

    • 如果 Where 過濾后沒有元素,Min() 會拋出 InvalidOperationException
    • 安全處理方式:
      var minAge = list.Where(e => e.Age > 30).Select(e => e.Age).DefaultIfEmpty(0).Min();
      
  2. 性能優化

    • 對于大型集合,考慮使用更高效的算法:
      int? minAge = null;
      foreach (var e in list)
      {if (e.Age > 30 && (minAge == null || e.Age < minAge)){minAge = e.Age;}
      }
      

其他聚合方法鏈式調用示例

1. 計算平均值

double avgSalary = employees.Where(e => e.Department == "Sales").Average(e => e.Salary);

2. 求和統計

decimal totalSales = salesRecords.Where(s => s.Year == 2023).Sum(s => s.Amount);

3. 計數統計

int highEarners = employees.Where(e => e.Salary > 100000).Count();

4. 多級聚合

var stats = employees.GroupBy(e => e.Department).Select(g => new {Department = g.Key,MinSalary = g.Min(e => e.Salary),MaxSalary = g.Max(e => e.Salary),AvgSalary = g.Average(e => e.Salary)});

鏈式調用的高級應用

1. 條件聚合

var result = products.Where(p => p.Category == "Electronics").Select(p => p.Price).DefaultIfEmpty(0) // 處理空集合.Average();

2. 組合使用

var analysis = orders.Where(o => o.Date.Year == 2023).GroupBy(o => o.CustomerId).Select(g => new {CustomerId = g.Key,TotalOrders = g.Count(),TotalAmount = g.Sum(o => o.Amount),AvgOrderValue = g.Average(o => o.Amount)}).OrderByDescending(x => x.TotalAmount).Take(10);

3. 空值處理技巧

decimal? maxDiscount = customers.Where(c => c.IsPremium).Select(c => c.DiscountPercentage).Where(d => d.HasValue).DefaultIfEmpty(0).Max();

分組:

GroupBy()方法參數是分組條件表達式,返回值為IGrouping<TKey,TSource>類型的泛型IEnumerable,也就是每一組以一個IGrouping對象的形式返回。IGrouping是一個繼承自IEnumerable的接口,IGrouping中Key屬性表示這一組的分數的值。例子:根據年齡分組,獲取每組人數、最高工資、平均工資。

C# LINQ GroupBy 分組方法詳解

GroupBy 方法核心概念

GroupBy() 是 LINQ 中最強大的數據分組方法,它允許您根據指定的鍵將數據集合劃分為多個邏輯組。

方法簽名

IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IEnumerable<TSource> source,Func<TSource, TKey> keySelector
)

關鍵特性

特性說明
分組條件通過 keySelector 函數指定分組依據
返回值IEnumerable<IGrouping<TKey, TSource>>
分組對象每個分組是一個 IGrouping<TKey, TSource> 對象
分組訪問可以通過 Key 屬性訪問分組鍵
元素訪問分組本身是可枚舉的,包含該組的所有元素

IGrouping 接口解析

IGrouping<TKey, TSource> 接口定義如下:

public interface IGrouping<out TKey, out TElement> : IEnumerable<TElement>
{TKey Key { get; }
}

核心特性

  1. 繼承自 IEnumerable

    • 每個分組本身是一個可枚舉集合
    • 可以遍歷分組內的所有元素
  2. Key 屬性

    • 表示該分組的鍵值
    • 類型為 TKey,由分組條件決定

基本用法示例

1. 簡單分組

List<Employee> employees = new List<Employee>
{new Employee { Name = "Alice", Department = "HR", Salary = 50000 },new Employee { Name = "Bob", Department = "IT", Salary = 60000 },new Employee { Name = "Charlie", Department = "HR", Salary = 55000 },new Employee { Name = "David", Department = "IT", Salary = 70000 }
};// 按部門分組
var groups = employees.GroupBy(e => e.Department);foreach (var group in groups)
{Console.WriteLine($"部門: {group.Key}");foreach (var emp in group){Console.WriteLine($" - {emp.Name}: {emp.Salary}");}
}

輸出結果

部門: HR- Alice: 50000- Charlie: 55000
部門: IT- Bob: 60000- David: 70000

2. 分組后聚合計算

var departmentStats = employees.GroupBy(e => e.Department).Select(g => new {Department = g.Key,EmployeeCount = g.Count(),AverageSalary = g.Average(e => e.Salary),MaxSalary = g.Max(e => e.Salary)});foreach (var stat in departmentStats)
{Console.WriteLine($"{stat.Department}部門: " +$"人數={stat.EmployeeCount}, " +$"平均工資={stat.AverageSalary}, " +$"最高工資={stat.MaxSalary}");
}

輸出結果

HR部門: 人數=2, 平均工資=52500, 最高工資=55000
IT部門: 人數=2, 平均工資=65000, 最高工資=70000

高級分組技巧

1. 復合鍵分組

// 按部門和薪資范圍分組
var groups = employees.GroupBy(e => new {e.Department,SalaryRange = e.Salary / 10000 * 10000 // 按萬為單位分組
});foreach (var group in groups)
{Console.WriteLine($"部門: {group.Key.Department}, " +$"薪資范圍: {group.Key.SalaryRange}-{group.Key.SalaryRange + 9999}");foreach (var emp in group){Console.WriteLine($" - {emp.Name}: {emp.Salary}");}
}

2. 分組后元素轉換

// 分組后只保留員工姓名
var nameGroups = employees.GroupBy(e => e.Department, e => e.Name); // 元素選擇器foreach (var group in nameGroups)
{Console.WriteLine($"部門: {group.Key}");Console.WriteLine($"員工: {string.Join(", ", group)}");
}

3. 自定義結果選擇器

var results = employees.GroupBy(keySelector: e => e.Department,resultSelector: (key, elements) => new {Department = key,Employees = elements.Select(e => e.Name),TotalSalary = elements.Sum(e => e.Salary)});foreach (var result in results)
{Console.WriteLine($"{result.Department}部門: " +$"總薪資={result.TotalSalary}, " +$"員工={string.Join(", ", result.Employees)}");
}

IGrouping 的實際應用

1. 直接訪問分組鍵

var groups = employees.GroupBy(e => e.Department);// 獲取所有部門列表
var departments = groups.Select(g => g.Key).ToList();
// ["HR", "IT"]

2. 分組嵌套處理

foreach (var group in groups)
{Console.WriteLine($"--- {group.Key} 部門員工詳情 ---");// 分組內排序var sortedEmployees = group.OrderByDescending(e => e.Salary);foreach (var emp in sortedEmployees){Console.WriteLine($"{emp.Name}: {emp.Salary}");}
}

3. 轉換為字典

// 將分組轉換為字典
Dictionary<string, List<Employee>> departmentDict = groups.ToDictionary(g => g.Key, g => g.ToList());// 訪問特定部門
var hrEmployees = departmentDict["HR"];

性能注意事項

  1. 延遲執行

    • GroupBy() 是延遲執行方法
    • 實際分組操作在枚舉結果時發生
  2. 內存占用

    • 分組操作需要將整個數據集加載到內存
    • 大數據集考慮使用數據庫分組
  3. 數據庫優化

    • 在 Entity Framework 中,GroupBy() 會轉換為 SQL 的 GROUP BY
    • 確保分組字段有索引
// EF Core 中的分組
var departmentStats = dbContext.Employees.GroupBy(e => e.Department).Select(g => new {Department = g.Key,Count = g.Count()}).ToList();

投影:

把集合中的每一項轉換為另外一種類型。
IEnumerable names = list.Select(e=> e.Gender?“男”:“女”);
var dogs = list.Select(p => new Dog { NickName = e.Name, Age = e.Age });

C# LINQ 投影操作詳解

投影的本質

投影(Projection)是 LINQ 中的核心概念,指的是將集合中的每個元素轉換為另一種形式或類型的操作。這類似于數學中的映射函數,將輸入集合中的每個元素映射到輸出集合中的新元素。

C# LINQ 投影操作詳解

投影的本質

投影(Projection)是 LINQ 中的核心概念,指的是將集合中的每個元素轉換為另一種形式或類型的操作。這類似于數學中的映射函數,將輸入集合中的每個元素映射到輸出集合中的新元素。

核心方法:Select()

Select() 方法是 LINQ 中實現投影的主要方式,它允許您:

  • 提取對象的特定屬性
  • 創建新的對象結構
  • 執行計算并返回結果
  • 轉換數據類型

方法簽名

IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source,Func<TSource, TResult> selector
)

投影的基本用法

1. 提取屬性值

List<Employee> employees = new List<Employee>
{new Employee { Name = "Alice", Age = 30 },new Employee { Name = "Bob", Age = 25 }
};// 投影到名字列表
IEnumerable<string> names = employees.Select(e => e.Name);
// 結果: ["Alice", "Bob"]

2. 創建新對象

// 投影到匿名對象
var employeeInfos = employees.Select(e => new {Name = e.Name,BirthYear = DateTime.Now.Year - e.Age
});
// 結果: [{Name="Alice", BirthYear=1993}, {Name="Bob", BirthYear=1998}]

3. 轉換類型

// 轉換為DTO對象
List<EmployeeDTO> dtos = employees.Select(e => new EmployeeDTO {EmployeeName = e.Name,Age = e.Age
}).ToList();

高級投影技巧

1. 帶索引的投影

// 包含元素索引
var indexed = employees.Select((e, index) => new {Index = index,e.Name
});
// 結果: [{Index=0, Name="Alice"}, {Index=1, Name="Bob"}]

2. 嵌套投影

// 嵌套集合投影
var departments = new List<Department>
{new Department {Name = "Dev",Employees = new List<Employee> { /* ... */ }}
};var employeeNamesByDept = departments.Select(d => new {DeptName = d.Name,EmployeeNames = d.Employees.Select(e => e.Name)
});

3. 條件投影

// 根據條件返回不同投影
var mixed = employees.Select(e => e.Age > 25 ? new { e.Name, Category = "Senior" } : new { e.Name, Category = "Junior" });

4. 計算字段投影

// 計算年薪(月薪*12)
var annualSalaries = employees.Select(e => new {e.Name,AnnualSalary = e.MonthlySalary * 12
});

實際應用場景

1. 數據轉換(Entity → DTO)

// 數據庫實體轉視圖模型
var viewModels = dbContext.Products.Where(p => p.Price > 100).Select(p => new ProductViewModel {Id = p.Id,Name = p.Name,Price = p.Price * 1.1 // 添加增值稅}).ToList();

2. 數據簡化

// 只選擇需要的字段
var lightweights = bigList.Select(item => new {item.Id,item.CreatedDate
});

3. 計算字段

// 計算BMI
var bmiData = persons.Select(p => new {p.Name,BMI = p.Weight / (p.Height * p.Height)
});

4. 組合數據

// 組合多個來源的數據
var combined = employees.Select(e => new {e.Name,DepartmentName = departments.First(d => d.Id == e.DeptId).Name
});

性能考慮

  1. 延遲執行

    • Select() 是延遲執行的,只有在實際枚舉結果時才會執行投影
  2. 高效轉換

    • 在數據庫查詢中(如 EF Core),Select() 會轉換為 SQL 的 SELECT 子句
    • 只選擇需要的字段可以減少數據傳輸量
  3. 避免重復計算

    // 低效:重復計算
    var inefficient = list.Select(x => new {Value = HeavyCalculation(x)});// 高效:預計算
    var efficient = list.Select(x => {var result = HeavyCalculation(x);return new { Value = result };});
    

與 SelectMany() 的區別

特性SelectSelectMany
輸入單個元素元素集合
輸出轉換后的單個元素展平的集合
嵌套集合返回嵌套集合展平嵌套集合
使用場景簡單轉換處理一對多關系
// Select 返回嵌套集合
var nested = departments.Select(d => d.Employees.Select(e => e.Name));// SelectMany 展平嵌套集合
var flat = departments.SelectMany(d => d.Employees.Select(e => e.Name));

最佳實踐

  1. 明確目標類型

    • 使用具體類型而非 var 提高可讀性
    List<string> names = employees.Select(e => e.Name).ToList();
    
  2. 避免過度投影

    • 只選擇真正需要的字段
    • 避免選擇整個對象再丟棄不需要的字段
  3. 結合過濾

    // 先過濾再投影,提高效率
    var activeUsers = users.Where(u => u.IsActive).Select(u => u.Email);
    
  4. 使用查詢語法

    // 與方法語法等效
    var results = from e in employeesselect new { e.Name, e.Age };
    

集合轉換:

有一些地方需要數組類型或者List類型的變量,我們可以用ToArray()方法和ToList()分別把IEnumerable<T>轉換為數組類型和List<T>類型。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/95567.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/95567.shtml
英文地址,請注明出處:http://en.pswp.cn/web/95567.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

第 2 講:Kafka Topic 與 Partition 基礎

課程概述 在第一篇課程中&#xff0c;我們了解了 Kafka 的基本概念和簡單的 Producer/Consumer 實現。 本篇課程將深入探討 Kafka 的核心機制&#xff1a;Topic 和 Partition。 學習目標 通過本課程&#xff0c;您將掌握&#xff1a; Topic 和 Partition 的設計原理&#x…

三階Bezier曲線曲率極值及對應的u的計算方法

三階&#xff08;三次&#xff09;Bezier曲線的曲率極值及其對應的參數 u 的計算是一個復雜的非線性優化問題。由于三階Bezier曲線是參數化曲線&#xff0c;其曲率表達式較為復雜&#xff0c;通常無法通過解析方法直接求得所有極值點&#xff0c;但可以通過求解曲率導數為零的方…

Unity:XML筆記(二)——Xml序列化、反序列化、IXmlSerializable接口

寫在前面&#xff1a;寫本系列(自用)的目的是回顧已經學過的知識、記錄新學習的知識或是記錄心得理解&#xff0c;方便自己以后快速復習&#xff0c;減少遺忘。三、Xml序列化序列化就是把想要存儲的內容轉換為字節序列用于存儲或傳遞。1、序列化我們先創建一個類&#xff0c;之…

java注解、Lambda表達式、Servlet

一、Java注解注解的概念&#xff1a; Java注解是代碼中的元數據&#xff0c;可以用于描述其他代碼。注解在編譯、類加載、運行時被處理&#xff0c;并且不會改變代碼邏輯。注解的用途&#xff1a; 提供代碼元信息&#xff0c;如 Override 表明一個方法覆蓋了父類的方法。 編譯檢…

【單片機day02】

GPIO&#xff1a;Genral Purpose Input/Output&#xff0c;GPIO是51單片機和外界交互最基本的方式工作模式&#xff1a;輸出模式&#xff1a;單片機給定引腳一個電平(高電平(5V) 低電平(0V)),控制引腳實現高低電平輸入模式&#xff1a;檢測引腳電平變化GPIO水龍頭輸出模式&…

Java中最常用的設計模式

Java設計模式之結構型—代理模式-CSDN博客 觀察者模式詳解-CSDN博客 單例模式詳解-CSDN博客 Java設計模式之結構型—享元模式-CSDN博客 Java設計模式之創建型—建造者模式-CSDN博客 Java設計模式之結構型—工廠模式-CSDN博客 Java設計模式之結構型—適配器模式-CSDN博客 …

使用Axure動態面板制作輪播圖案例詳解

在現代網頁設計中&#xff0c;輪播圖&#xff08;Carousel&#xff09;是一種常見且高效的展示方式&#xff0c;用于在同一空間內循環展示多張圖片或內容。Axure RP作為一款強大的原型設計工具&#xff0c;提供了動態面板和豐富的交互事件功能&#xff0c;使得制作輪播圖變得簡…

VUE的中 computed: { ...mapState([‘auditObj‘]), }寫法詳解

具體解析&#xff1a;computed&#xff1a;這是 Vue 組件選項中的計算屬性&#xff0c;用于聲明依賴于其他數據而存在的派生數據。計算屬性會根據依賴進行緩存&#xff0c;只有當依賴的數據發生變化時才會重新計算。mapState&#xff1a;這是 Vuex 提供的一個輔助函數&#xff…

【ProtoBuf】以 “數據秘語” 筑聯絡:通訊錄項目實戰 1.0 啟步札記

文章目錄引言筑路之備&#xff1a;快速上手ProtoBuf步驟一&#xff1a;創建.proto文件?件規范添加注釋指定 proto3 語法package 聲明符定義消息&#xff08;message&#xff09;定義消息字段【定義聯系人 message】字段唯一編號的范圍步驟2&#xff1a;編譯 contacts.proto ?…

在 macOS 下升級 Python 幾種常見的方法

在 macOS 下升級 Python 有幾種常見的方法&#xff0c;具體取決于你最初是如何安裝 Python 的。了解你的安裝方式是關鍵。 首先&#xff0c;你需要知道你當前 Python 版本以及它的安裝路徑。 檢查 Python 版本&#xff1a; python --version # 可能指向 Python 2.x python3 …

Linux 入門到精通,真的不用背命令!零基礎小白靠「場景化學習法」,3 個月拿下運維 offer,第二十五天

三、Shell腳本編程 Shell腳本語言的運算 算數運算 shell支持算術運算&#xff0c;但只支持整數&#xff0c;不支持小數 Bash中的算術運算 -- 加法運算 -- - 減法運算 -- * 乘法運算 -- / 除法運算 -- % 取模&#xff0c;即取余數 -- ** 乘方 ? #乘法符號在有些場景需要轉…

SpringAI系列---【多租戶記憶和淘汰策略】

1.多租戶工作原理 2.引入jdbc的pom spring官網鏈接&#xff1a;https://docs.spring.io/spring-ai/reference/api/chat-memory.html&#xff0c;推薦使用官網的jdbc。 阿里巴巴ai鏈接&#xff1a;https://github.com/alibaba/spring-ai-alibaba/tree/main/community/memories j…

Linux gzip 命令詳解:從基礎到高級用法

Linux gzip 命令詳解&#xff1a;從基礎到高級用法 在 Linux 系統中&#xff0c;文件壓縮與解壓縮是日常運維和文件管理的常見操作。gzip&#xff08;GNU Zip&#xff09;作為一款經典的壓縮工具&#xff0c;憑借其高效的壓縮算法和簡潔的使用方式&#xff0c;成為 Linux 用戶處…

Redis有什么優點和缺點?

優點&#xff1a;極致性能&#xff1a; 基于內存操作和高效的單線程 I/O 模型&#xff0c;讀寫速度極快。數據結構豐富&#xff1a; 支持多種數據結構&#xff0c;如 String、Hash、List、Set、ZSet、Stream、Geo 等&#xff0c;編程模型靈活。持久化與高可用&#xff1a; 提供…

NestJS 3 分鐘搭好 MySQL + MongoDB,CRUD 復制粘貼直接運行

基于上一篇內容《為什么現代 Node 后端都選 NestJS TypeScript&#xff1f;這組合真香了》&#xff0c;這篇文章繼續寫數據庫的連接。 所以今天把MySQL、MongoDB全接上&#xff0c;做個小實例。朋友們項目里用什么數據庫可以視情況而定。 這里的功能分別為&#xff1a; MySQ…

用了企業微信 AI 半年,這 5 個功能讓我徹底告別重復勞動

每天上班不是在整理會議紀要&#xff0c;就是在翻聊天記錄找文件&#xff0c;寫文檔還要自己摳數據…… 這些重復勞動是不是也在消耗你的時間&#xff1f;作為用了企業微信 AI 功能半年的 “老用戶”&#xff0c;我必須說&#xff1a;企業微信 AI 的這 5 個功能&#xff0c;真的…

從入門到高手,Linux就應該這樣學【好書推薦】

從入門到高手&#xff0c;請這樣學Linux 一、Linux基礎與終端操作 1.1 Linux簡介 Linux 是一種開源的類 Unix 操作系統&#xff0c;以其穩定性、安全性和高效性被廣泛應用于服務器、嵌入式系統及開發環境中。掌握基本命令和操作技巧是 Linux 學習的關鍵。 1.2 終端基礎 打開…

【數據可視化-104】安徽省2025年上半年GDP數據可視化分析:用Python和Pyecharts打造炫酷大屏

&#x1f9d1; 博主簡介&#xff1a;曾任某智慧城市類企業算法總監&#xff0c;目前在美國市場的物流公司從事高級算法工程師一職&#xff0c;深耕人工智能領域&#xff0c;精通python數據挖掘、可視化、機器學習等&#xff0c;發表過AI相關的專利并多次在AI類比賽中獲獎。CSDN…

組件庫UI自動化

一、背景 背景&#xff1a; 組件庫全局改動場景多&#xff0c;組件之間耦合場景多–時常需要全場景回歸組件庫demo有200多個&#xff0c;手動全局回歸耗時耗力細微偏差純視覺無法辨別 可行性分析&#xff1a; 組件庫功能占比 L1&#xff08;視覺層&#xff09;&#xff1a;圖片…

面試題:JVM與G1要點總結

一.Java內存區域 1.運行時數據區的介紹 2.站在線程的角度看Java內存區域 3.深入分析堆和棧的區別 4.方法的出入棧和棧上分配、逃逸分析及TLAB 5.虛擬機中的對象創建步驟 6.對象的內存布局 1.運行時數據區的介紹 運行時數據區的類型&#xff1a;程序計數器、Java虛擬機棧、本地方…