C# 集合

简介

集合表示一组具有某种性质的数学元素,引用到程序设计中表示一组具有相同性质的对象。集合的大小可以动态调整,也可以在运行时添加或删除元素

官方文档

集合基类接口

  • ICollection
  • IEnumerable
  • IQueryable

集合列表

常用集合

数组

--空数组
Enumerable.Empty<int>().ToArray();
new List<int>().ToArray();

int[] arr = { 1, 2 };
int[] array = new int[] { 10, 20, 30 };
int[] array = new [] { 10, 20, 30 };

arr.Append(3);//添加
Array.Find(arr,i => i == 1);//查找某个元素
arr= Array.FindAll(arr, i => i != 1).ToArray();//删除
Console.WriteLine(String.Join(",", arr));//以逗号间隔输出数组

ArrayList

ArrayList list = new ArrayList();
list.Add(10);//单个添加
foreach (int number in new int[5] { 1, 2, 3, 4, 5 })
{
    list.Add(number);//循环添加一个数组
}
int[] number2 = new int[2] { 11, 12 };
list.AddRange(number2);//集体添加
list.Remove(3);//删除值为3的
list.RemoveAt(3);//移除下标为3的
ArrayList list2 = new ArrayList(list.GetRange(1, 3));//新ArrayList(list2)只取旧ArrayList(list)中的一部份List<T>.GetRange(Index, Count)从下标为1的开始取三个
Console.WriteLine("Method One:");
foreach (int i in list)
{
    Console.WriteLine(i);//遍历方法一
}
Console.WriteLine("Method Two:");
for (int i = 0; i != list2.Count; i++)//数组是length
{
    int number = (int)list2[i];//一定要强制转换
    Console.WriteLine(number);//遍历方法二
}

BitArray

 BitArray 类管理一个紧凑型的位值数组,它使用布尔值来表示,其中 true 表示位是开启的(1),false 表示位是关闭的(0)。当您需要存储位,但是事先不知道位数时,则使用点阵列。您可以使用整型索引从点阵列集合中访问各项,索引从零开始。
 

 // 创建两个大小为 8 的点阵列
BitArray ba1 = new BitArray(8);
BitArray ba2 = new BitArray(8);
byte[] a = { 60 };
byte[] b = { 13 };
// 把值 60 和 13 存储到点阵列中
ba1 = new BitArray(a);
ba2 = new BitArray(b);
// ba1 的内容
Console.WriteLine("Bit array ba1: 60");
for (int i = 0; i < ba1.Count; i++)
{
    Console.Write("{0, -6} ", ba1[i]);
}
Console.WriteLine();

// ba2 的内容
Console.WriteLine("Bit array ba2: 13");
for (int i = 0; i < ba2.Count; i++)
{
    Console.Write("{0, -6} ", ba2[i]);
}
Console.WriteLine();


BitArray ba3 = new BitArray(8);
ba3 = ba1.And(ba2);

// ba3 的内容
Console.WriteLine("Bit array ba3 after AND operation: 12");
for (int i = 0; i < ba3.Count; i++)
{
    Console.Write("{0, -6} ", ba3[i]);
}
Console.WriteLine();

ba3 = ba1.Or(ba2);
// ba3 的内容
Console.WriteLine("Bit array ba3 after OR operation: 61");
for (int i = 0; i < ba3.Count; i++)
{
    Console.Write("{0, -6} ", ba3[i]);
}

List

var list = new List<string> { "a", "c" };

字典

//New方法
var dicA = new Dictionary<int, string>
{
    [1] = "a",
    [5] = "e"
};
//Old方法
var dicB = new Dictionary<int, string>
{
    {1, "a"},
    {5, "b"}
};

//判断Key是否存在
dicA.ContainsKey(1);
//根据键获取值
dicA.GetValueOrDefault(1);
string stra = string.Empty;
dicA.TryGetValue(1, out stra);

//遍历字典
foreach (KeyValuePair<int, string> kvp in dicA)
{
    Console.WriteLine(kvp.Key + kvp.Value);
}

//获取哈希表中键集合
Dictionary<int, string>.KeyCollection keyColl = dicA.Keys;

//获取哈希表值集合
Dictionary<int, string>.ValueCollection valueColl = dicA.Values;

//Linq获取第一个值并判断是否非空
KeyValuePair<int, string> result = dicA.FirstOrDefault(kv => kv.Key == 2);

if (!result.Equals(default(KeyValuePair<int, string>)))
{
		Console.WriteLine("非空");
}
else
{
		Console.WriteLine("等于空");
}

//转字典
Dictionary<string, string> dic = dt.AsEnumerable().ToDictionary(row => row["Id"].ToString(), row => row["Name"].ToString());
string[] str = new string[] { "Car", "Bus", "Bicycle" };
var d = str.ToDictionary(item => item, item => true);
//ToDictionary分组
var f= str.GroupBy(k=>k).ToDictionary(k => k.Key, v => v.Count());

//转字典
Dictionary<string, string> dic = dt.AsEnumerable().ToDictionary(row => row["Id"].ToString(), row => row["Name"].ToString());
string[] str = new string[] { "Car", "Bus", "Bicycle" };
var d = str.ToDictionary(item => item, item => true);
//ToDictionary分组
var f= str.GroupBy(k=>k).ToDictionary(k => k.Key, v => v.Count());

//转元祖字典
        Dictionary<string, List<Tuple<string, string, string>>> dataDic = detail.AsEnumerable()
.GroupBy(row => row.Field<string>("SampleId"))
.ToDictionary(
    group => group.Key,
    group => group.Select(row => new Tuple<string, string, string>(
        row["Code"].ToString(),
        row["STR1"].ToString(),
        row["STR9"].ToString()
    )).ToList());

//List转字典,Key重复取第一条
tuples.GroupBy(tuple => tuple.Item2).ToDictionary(group => group.Key, group => group.First().Item1);

KeyValuePair

List<KeyValuePair<int, string>> pairs = new List<KeyValuePair<int, string>>();
pairs.Add(new KeyValuePair<int, string>(1, "apple"));
pairs.Add(new KeyValuePair<int, string>(2, "cherry"));
pairs.Add(new KeyValuePair<int, string>(2, "date")); // 重复的键
pairs.Add(new KeyValuePair<int, string>(3, "fig"));
pairs.Add(new KeyValuePair<int, string>(3, "grape")); // 重复的键
pairs.Add(new KeyValuePair<int, string>(4, "honeydew"));
pairs.Add(new KeyValuePair<int, string>(5, "kiwi"));
pairs.Add(new KeyValuePair<int, string>(5, "lemon")); // 重复的键

foreach (KeyValuePair<int, string> pair in pairs)
{
    Console.WriteLine("Key: " + pair.Key + ", Value: " + pair.Value);
}

Hashtable

Hashtable 遍历是无序的

Hashtable ht = new Hashtable();

ht.Add("1", "hello");
ht.Add("2", "world");
ht.Add("3", "I");
ht.Add("4", "Love");
ht.Add("5", "China");

//遍历方法一:遍历哈希表中的键
foreach (string key in ht.Keys)
{
    //Console.WriteLine(string.Format("{0}-{1}"), key, ht[key]);
    Console.WriteLine(string.Format("{0}-{1}", key, ht[key]));
}
Console.WriteLine("**********************************************************");
//遍历方法二:遍历哈希表中的值
foreach (string value in ht.Values)
{
    Console.WriteLine(value);
}
Console.WriteLine("**********************************************************");
//遍历方法三:遍历哈希表中的键值
foreach (DictionaryEntry de in ht)
{
    Console.WriteLine(string.Format("{0}-{1}", de.Key, de.Value));
}

HashSet

HashSet是无序的

int[] arr = { 1, 2, 3 };
HashSet<int> hashSet = new HashSet<int>(arr);
hashSet.Add(4);

foreach (var city in hashSet)
{
    Console.WriteLine(city);
}

//判断是否存在
hashSet.Contains(5);//false

//判断集合是否存在交集(类似Any)
int[] newarr = { 3, 4 };
hashSet.Overlaps(newarr);//true

//判断两个集合是否完全一致(顺序不对也是false)
HashSet<int> hashSet2 = new HashSet<int>(newarr);
hashSet.SetEquals(hashSet2); 

//集合Join
HashSet<int> set1 = new HashSet<int> { 1, 2, 3, 4 };
HashSet<int> set2 = new HashSet<int> { 3, 4, 5, 6 };
//set1.UnionWith(set2);//1-6
//set1.ExceptWith(set2);//1,2
//set1.IntersectWith(set2);//3,4
//set1.SymmetricExceptWith(set2);//1,2,5,6

线程安全

//同步功能可以使HashTable(某种程度上)线程安全。这将在HashTable周围创建一个包装器,仅允许一个写入器和多个读取器访问HashTable。
//但是HashTable包装器不是完全线程安全的,一个线程可以迭代集合,而另一个线程仍然可以并发地更改集合,从而导致异常。

//创建一个同步(线程安全)的 Hashtable
Hashtable syncdHT = Hashtable.Synchronized(new Hashtable());

//判断是否线程安全
Console.WriteLine("myHT is {0}.", new Hashtable().IsSynchronized ? "synchronized" : "not synchronized");
Console.WriteLine("syncdHT is {0}.", syncdHT.IsSynchronized ? "synchronized" : "not synchronized");

Queue

队列(Queue)代表了一个先进先出的对象集合。enqueue方法入队列,dequeue方法出队列


Queue q = new Queue();

q.Enqueue('A');
q.Enqueue('B');
q.Enqueue('C');
q.Enqueue('D');

Console.WriteLine("Current queue: ");//队列先进先出
foreach (char value in q)
    Console.Write(value + " ");
Console.WriteLine();
q.Enqueue('V');//向 Queue 的末尾添加一个对象。
q.Enqueue('H');
Console.WriteLine("Current queue: ");
foreach (char value in q)
    Console.Write(value + " ");
Console.WriteLine();
Console.WriteLine("Removing some values ");
char ch = (char)q.Dequeue();//移除并返回在 Queue 的开头的对象。
Console.WriteLine("The removed value: {0}", ch);
ch = (char)q.Dequeue();
Console.WriteLine("The removed value: {0}", ch);

Stack

堆栈(Stack)代表了一个后进先出的对象集合。Push方法入栈,Pop方法出栈。

Stack st = new Stack();

st.Push('A');
st.Push('B');
st.Push('C');
st.Push('D');

Console.WriteLine("Current stack: ");//当前队列,先存后出
foreach (char value in st)
{
    Console.Write(value + " ");
}
Console.WriteLine();
st.Push('V');//向 Stack 的顶部添加一个对象。
st.Push('H');
Console.WriteLine("The next poppable value in stack: {0}", st.Peek());//返回在 Stack 的顶部的对象,但不移除它。
Console.WriteLine("Current stack: ");
foreach (char value in st)
{
    Console.Write(value + " ");
}
Console.WriteLine();
Console.WriteLine("Removing values ");
st.Pop();//移除并返回在 Stack 的顶部的对象
st.Pop();
st.Pop();

Console.WriteLine("Current stack: ");
foreach (char value in st)
{
    Console.Write(value + " ");
}

LinkedList

//链表通常用于需要频繁插入和删除元素的场景,但不适用于需要随机访问元素的场景
LinkedList<string> list = new LinkedList<string>();

list.AddLast("apple");
list.AddLast("banana");
list.AddLast("cherry");
LinkedListNode<string> node = list.Find("banana");
list.AddBefore(node, "orange");
list.Remove(node);

foreach (string item in list)
{
    Console.WriteLine(item);
}
Console.WriteLine(list.Count);

SortedList

SortedList sl = new SortedList();

sl.Add("1", "hello");
sl.Add("2", "world");
sl.Add("3", "I");
sl.Add("4", "Love");

foreach (var item in sl.Keys)
{
    Console.WriteLine(item + ":" + sl[item]);
}
int myIndex = 1;
Console.WriteLine("The key at index {0} is {1}.", myIndex, sl.GetKey(myIndex));//获得下标为1的键的名称
Console.WriteLine("The value at index {0} is {1}.", myIndex, sl.GetByIndex(myIndex));//获得键为1的值

PriorityQueue

优先级出队

PriorityQueue<string, int> priorityQueue = new();

priorityQueue.Enqueue("Second", 2);
priorityQueue.Enqueue("Fourth", 4);
priorityQueue.Enqueue("Third 1", 3);
priorityQueue.Enqueue("Third 2", 3);
priorityQueue.Enqueue("First", 1);

while (priorityQueue.Count > 0)
{
    string item = priorityQueue.Dequeue();
    Console.WriteLine(item);
}

// Output:
// First
// Second
// Third 2
// Third 1
// Fourth

Channel

using Microsoft.IO;
using System.Threading.Channels;

namespace ConsoleApp1;

public class Program
{

    static void Main(string[] args)
    {
        //不限容Channel
        var channel = Channel.CreateUnbounded<int>();
        //限容Channel
        var channelBounded = Channel.CreateBounded<string>(new BoundedChannelOptions(1000) { FullMode = BoundedChannelFullMode.Wait });

    }

    public static async Task SingleProducerSingleConsumer()
    {
        var channel = Channel.CreateUnbounded<int>();

        // 生产者写入消息
        channel.Writer.TryWrite(1);
        await channel.Writer.WriteAsync(2);

        //生产者也可以明确告知消费者不会发送任何消息了
        channel.Writer.Complete();

        //当有空间可写时,返回一个true。因为Channel有限容类型的Channel,所以这个方法也可以作为一个屏障,当Channel空间已满时,进行阻塞
        if (await channel.Writer.WaitToWriteAsync()) { }


        //async stream,在没有被生产者明确Complete的情况下,这里会一致阻塞下去
        await foreach (var item in channel.Reader.ReadAllAsync())
        {
            Console.WriteLine(item);
        }
    }

    public static async Task SingleProducerSingleConsumer2()
    {
        var channel = Channel.CreateUnbounded<int>();

        Task.Run(async () =>
        {
            for (int i = 0; i < 10; i++)
            {
                await channel.Writer.WriteAsync(i);
                if (i > 5)
                {
                    channel.Writer.Complete();
                }
            }
        });

        Task.Run(async () =>
        {
            await foreach (var item in channel.Reader.ReadAllAsync())
            {
                Console.WriteLine(item);
            }
        });
    }

    public static async Task SingleProducerSingleConsumer3()
    {
        var channel = Channel.CreateUnbounded<int>();
        var reader = channel.Reader;
        for (int i = 0; i < 10; i++)
        {
            await channel.Writer.WriteAsync(i + 1);
        }

        while (await reader.WaitToReadAsync())
        {
            //var i2= await reader.ReadAsync();
            if (reader.TryRead(out var i))
            {
                Console.WriteLine(i);
            }
        }
    }
}

BlockingCollection

using System.Collections.Concurrent;

BlockingCollection<string> bc = new();

//先进先出(FIFO)
BlockingCollection<string> bc2 = new(new ConcurrentQueue<string>());

//先进后出(LIFO)
BlockingCollection<int> bc3 = new(new ConcurrentStack<int>());

//生产者
Task.Factory.StartNew(async () =>
{
    int count = 0;
    while (true)
    {
        bc.Add("String: " + count);
        count++;
        await Task.Delay(1000 * 5);

        //if (count > 10)
        //{
        //    bc.CompleteAdding();  //代表添加完成
        //}
    }
});

//消费者
Task.Factory.StartNew(async () =>
{
    while (true)
    {
        foreach (var element in bc.GetConsumingEnumerable())
        {
            Console.WriteLine("Work: " + element);
        }
        await Task.Delay(1000 * 5);
    }
});

元组

元组不是集合

官方文档

var user = ("小红", 18);
Console.WriteLine($"Name:{user.Item1}, Age:{user.Item2}");
var userDetails = (Age: 30, Name: "Ben", TestScore: 50f);

//ValueTuple是值类型,Tuple是引用类型
Tuple<string, int> tuple = new Tuple<string, int>("小红", 18);
ValueTuple<string, int> valueTuple = new ValueTuple<string, int>("小红", 18);

//集合
List<Tuple<string, int>> _tuples=new List<Tuple<string, int>>();
_tuples.Add(new Tuple<string, int> ( "", 1 ));

Span

参考文档

转换

string[] arr = { "a" };
//List直接转换
var span = System.Runtime.InteropServices.CollectionsMarshal.AsSpan(arr.ToList());
//数组直接转换
var span2 = arr.AsSpan();

使用

foreach (var item in CollectionsMarshal.AsSpan(_list))  {  }
for (int i = 0; i < span.Length; i++)  {  _ = span[i];  }  

请注意,在使用过程List或数组中不应添加和删除对象。

集合常用方法

常用

//判断一个string集合的字符串是否包含另一个集合的某一个元素
List<string> list1 = new List<string> { "apple", "banana", "orange" };
List<string> list2 = new List<string> { "an", "ra", "ge" };
bool isContain = list1.Any(str => list2.Any(subStr => str.Contains(subStr)));

排序

排序
myList = myList.OrderBy(c => c.Code).ToList();
myList.Sort((p1, p2) => string.Compare(p1.Name, p2.Name));
myList.Sort((p1, p2) => p1.Age.CompareTo(p2.Age));

倒序
myList = myList.OrderByDescending(c => c.Code).ToList();
myList.Sort((p1, p2) => string.Compare(p2.Name, p1.Name));

数组关联查询

int[] arrA = { 1, 2, 3 };
int[] arrB = { 2, 4 };
var Result1 = arrA.Union(arrB); //1234
var Result2 = arrA.Concat(arrB); //12324
var Result3 = arrA.Intersect(arrB); //2
var Result4 = arrA.Except(arrB); //13
var Result5 = arrA.Except(arrB).Union(arrB.Except(arrA));//134

Take/Skip

int[] arr = { 0, 1, 2, 3, 4, 5 };
Console.WriteLine(string.Join(',',arr.Take(2))); //0,1
Console.WriteLine(string.Join(',', arr.Skip(2))); //2,3,4,5
Console.WriteLine(string.Join(',', arr.TakeLast(2))); //4,5
Console.WriteLine(string.Join(',', arr.SkipLast(2))); //0,1,2,3

var source= Enumerable.Range(1, 10);
source.ElementAt(^2); // returns 9
var xx = source.Take(7).Skip(2).ToList(); // returns 34567
//source.Take(..3) 代替 source.Take(3)
//source.Take(3..) 代替 source.Skip(3)
//source.Take(2..7) 代替 source.Take(7).Skip(2)
//source.Take(^3..) 代替 source.TakeLast(3)
//source.Take(..^3) 代替 source.SkipLast(3)
//source.Take(^7..^3)而不是.source.TakeLast(7).SkipLast(3)

分页

var arr = Enumerable.Range(1, 100);
int pageindex = 3;
int pageSize = 5;
var items = arr.Skip((pageindex - 1) * pageSize).Take(pageSize);
foreach (var book in items)            
    Console.WriteLine(book);            

long pageCount = (long)Math.Ceiling(arr.LongCount() * 1.0 / pageSize);
Console.WriteLine("总页数" + pageCount);

EF Core分页

// <summary>
/// 分页方法
/// </summary>
/// <param name="pageindex">页码从1开始</param>
/// <param name="pageSize">每一页的数据条数</param>
static void printpage(int pageindex, int pageSize)
{
    using (MydatabaseContext mydatabase = new MydatabaseContext())
    {
        //过滤到不想要的数据
        //Contains()可以用来判断序列中是否存在指定的元素。
        //过滤掉不行要的数据
        IQueryable<TBook> books = mydatabase.TBooks.Where(a => !a.Bookname.Contains("李四"));

        //减一是因为要从零开始
        var items = books.Skip((pageindex - 1) * pageSize).Take(pageSize);
        foreach (var book in items)
        {
            Console.WriteLine(book.Bookname);
        }
        long count = books.LongCount();
        long pageCount = (long)Math.Ceiling(count * 1.0 / pageSize);
        Console.WriteLine("总页数" + pageCount);
    }
}

TakeWhile/SkipWhile

int[] nums = { 20, 15, 7, 11, 13, 4, 9 };

//获取第一个小于条件外的元素及后面的元素
//案例中25和15在条件内,不会获取到,7在条件外,会获取7及其后面所有元素
var result = nums.SkipWhile(n => n > 10);
foreach (var num in result)
    Console.WriteLine(num);


//获取开始部分到条件内的数据,当遇到第一个条件外元素时,后面元素不再判断
//案例中 123 在条件内,4因为不在条件内,4和后面元素都不会获取
var intList = new int[] { 1, 2, 3,  4, 5, -1, -2 };
var list = intList.TakeWhile(x => x <= 3);
foreach (var i in list)
    Console.WriteLine(i);

//i为元素坐标
var newList1 = intList.TakeWhile((p, i) => p <= 3 && i < 2);

Enumerable常用方法

官方文档
源码地址

Range/Repeat/Empty

//1,2,3...100
var a=Enumerable.Range(1,100).ToList();

//新建空对象
var b=Enumerable.Empty<int>().AsQueryable();

//添加重复数据
IEnumerable<int> ints = Enumerable.Repeat(1, 10);
//输出 1 1 1 1 1 1 1 1 1 1
foreach (int i in ints) Console.WriteLine(i);   

Append/Prepend

  • Append:向序列的尾部添加一个值。
  • Prepend:向序列的开头添加一个值。
var arr = Enumerable.Range(1, 2);
arr = arr.Append(3);
arr = arr.Prepend(4);
//4123
foreach (int i in arr) Console.WriteLine(i);

Index

//获取值的索引,获取索引值
var arr = new int[] { 1, 2, 3 };
var val = arr[1];
var index = Array.IndexOf(arr, 2);
var index2 = Array.LastIndexOf(arr, 2);
int index3 = Array.FindIndex(arr, (a) => a == 2);
int index4 = Array.FindLastIndex(arr, (a) => a == 2);

List<int> list = new List<int> { 1, 2, 3, 4, 5 };
int index5 = list.IndexOf(3);


//IEnumerable没有IndexOf方法,可以通过Array.IndexOf获取值的索引
var enumerable = Enumerable.Range(1, 20);
int value2 = enumerable.ElementAt(5);
var index6 = Array.IndexOf(enumerable.ToArray(), 2);

var arr = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var list = arr.ToList();
var index = list.IndexOf(5);
var index2 = list.FindIndex(X => X == 2);

list.GetRange(index, list.Count - index);//56789
list.GetRange(index + 1, list.Count - index - 1);//6789
list.GetRange(0, index);//1234
list.GetRange(0, index + 1);//12345
list.RemoveRange(0, index);//删除1234

Cast/OfType

ArrayList al = new ArrayList { 1, 2, "a" };
//会抛出异常
IEnumerable<int> IEInt = al.Cast<int>();
//自动移除无法转换的元素
IEnumerable<int> IEOfType = al.OfType<int>();
//输出 1 2
foreach (int i in IEOfType) Console.WriteLine(i);

Reverse

反转集合元素

var arr = Enumerable.Range(1, 5);
arr = arr.Reverse();
//54321
foreach (int i in arr) Console.Write(i);

Chunk

切片

// { {0,1,2}, {3,4,5}, {6,7,8}, {9} }
List<int[]> list = Enumerable.Range(1, 10).Chunk(3).ToList();

实现方法

public static IEnumerable<IEnumerable<T>> Chunk<T>(this IEnumerable<T> source, int chunksize)
{
    while (source.Any())
    {
        yield return source.Take(chunksize);
        source = source.Skip(chunksize);
    }
}

ZIP

int[] numbers = { 1, 2, 3, 4 };
string[] words = { "one", "two", "three" };
var numbersAndWords = words.Zip(numbers, (first, second) => first + " " + second);
var caxz = numbersAndWords.ToList();

var xs = Enumerable.Range(1, 10);
var ys = xs.Select(x => x.ToString());
var zs = xs.Select(x => x % 2 == 0);
foreach ((int x, string y, bool z) in Enumerable.Zip(xs, ys, zs)) { }

SequenceEqual

判断两个集合是否相等

IList<string> strList1 = new List<string>() { "One", "Two", "Three", "Four", "Three" };

IList<string> strList2 = new List<string>() { "One", "Two", "Three", "Four", "Three" };

bool isEqual = strList1.SequenceEqual(strList2); // returns true

IList<string> strList3 = new List<string>() { "One", "Two", "Three", "Four", "Three" };

IList<string> strList4 = new List<string>() { "Two", "One", "Three", "Four", "Three" };

bool isEqual2 = strList1.SequenceEqual(strList2); // returns false

SelectMany

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp2
{
    internal class Program
    {
        static void Main(string[] args)
        {
            List<Person> personList = new List<Person> {
                new Person{
                    Name = "P1", Age = 18, Gender = "Male",
                    Dogs = new Dog[] {
                        new Dog { Name = "D1" },
                        new Dog { Name = "D2" }
                    }
                },
                new Person{
                    Name = "P2", Age = 19, Gender = "Male",
                    Dogs = new Dog[]{
                        new Dog { Name = "D3" }
                    }
                },
                new Person{
                    Name = "P3", Age = 17,Gender = "Female",
                    Dogs = new Dog[]{
                        new Dog { Name = "D4" },
                        new Dog { Name = "D5" },
                        new Dog { Name = "D6" }
                    }
                }
            };
            var dogs1 = personList.SelectMany(p => p.Dogs);
            var dogs2 = personList.SelectMany((p, i) =>
                p.Dogs.Select(d =>
                {
                    d.Name = $"{i},{d.Name}";
                    return d;
                }));
            var results1 = personList.SelectMany(p => p.Dogs, (p, d) => new { PersonName = p.Name, DogName = d.Name });
            var results2 = personList.SelectMany((p, i) =>
            {
                for (int j = 0; j < p.Dogs.Length; j++)
                {
                    p.Dogs[j].Name = $"{i}-{p.Dogs[j].Name}";
                }
                return p.Dogs;
            }, (p, d) => new { PersonName = p.Name, DogName = d.Name });

        }
    }

    class Person
    {
        public string Name { set; get; }
        public int Age { set; get; }
        public string Gender { set; get; }
        public Dog[] Dogs { set; get; }
    }
    public class Dog
    {
        public string Name { set; get; }
    }
}

ThenBy

LINQ中的排序操作符,包括:OrderBy、OrderByDescending、ThenBy、ThenByDescending、Reverse,提供了升序或者降序排序。

  • OrderBy:按升序对序列的元素进行排序。
  • OrderByDescending:按降序对序列的元素排序。
  • ThenBy:按升序对序列中的元素执行后续排序。
  • ThenByDescending:按降序对序列中的元素执行后续排序。
List<Employee> result = new List<Employee>
{
    new Employee() { Name = "张伟伟", DeptID = 3, DeptName = "市场部", Salary = 1500, EntryTime = DateTime.Parse("2016-05-12") },
    new Employee() { Name = "李涛涛", DeptID = 2, DeptName = "财务部", Salary = 1600, EntryTime = DateTime.Parse("2017-02-16") },
    new Employee() { Name = "王亮亮", DeptID = 1, DeptName = "研发部", Salary = 1900, EntryTime = DateTime.Parse("2018-10-25") },
    new Employee() { Name = "孙红红", DeptID = 1, DeptName = "研发部", Salary = 1900, EntryTime = DateTime.Parse("2018-08-03") },
    new Employee() { Name = "黄苗苗", DeptID = 3, DeptName = "市场部", Salary = 2200, EntryTime = DateTime.Parse("2016-09-06") },
    new Employee() { Name = "蔡明明", DeptID = 1, DeptName = "研发部", Salary = 3500, EntryTime = DateTime.Parse("2012-11-25") },
    new Employee() { Name = "吴慧慧", DeptID = 2, DeptName = "财务部", Salary = 1800, EntryTime = DateTime.Parse("2018-07-26") },
    new Employee() { Name = "杨梅梅", DeptID = 3, DeptName = "市场部", Salary = 2200, EntryTime = DateTime.Parse("2017-02-15") }
};

//先按照部门ID排序,在按照薪资排序
result = result.OrderBy(a => a.DeptID).ThenByDescending(a => a.Salary).ThenBy(a => a.EntryTime).ToList();

/// <summary>
/// 员工信息类
/// </summary>
public class Employee
{
    /// <summary>
    /// 员工名称
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// 部门编号
    /// </summary>
    public int DeptID { get; set; }

    /// <summary>
    /// 部门名称
    /// </summary>
    public string DeptName { get; set; }

    /// <summary>
    /// 薪资
    /// </summary>
    public decimal Salary { get; set; }

    /// <summary>
    /// 入职时间
    /// </summary>
    public DateTime EntryTime { get; set; }
}

ToLookup

参考文档


List<Student> list = new List<Student>() {
    new Student() { Id = 1, Age = 1, Name = "11" },
    new Student() { Id = 1, Age = 2, Name = "22" },
    new Student() { Id = 2, Age = 2, Name = "22"},
    new Student() { Id = 2, Age = 3, Name = "33" },
    new Student() {Id = 2, Age = 4, Name = "44"  },
    new Student() {  Id = 3, Age = 3, Name = "33" },
    new Student() {  Id = 4, Age = 3, Name = "33" },
    new Student() {  Id = 4, Age = 5, Name = "33" },
};


var dic = list.ToLookup(m => m.Id);
foreach (var item in dic)
{
    Console.WriteLine("学生ID号:" + item.Key);

    foreach (var item1 in item)
    {
        Console.WriteLine("\t\t" + item1 + " || " + item1.Age + " || " + item1.Name);
    }
}

//输出:
//学生ID号: 1
//                Student || 1 || 11
//                Student || 2 || 22
//学生ID号: 2
//                Student || 2 || 22
//                Student || 3 || 33
//                Student || 4 || 44
//学生ID号: 3
//                Student || 3 || 33
//学生ID号: 4
//                Student || 3 || 33
//                Student || 5 || 33



public class Student
{
   public int Id { get; set; }
    
   public int Age { get; set; }

    public string Name { get; set; }
}
posted @ 2023-05-19 16:51  雨水的命运  阅读(52)  评论(0编辑  收藏  举报