# CSharp编程 **Repository Path**: fan-zhineng/csharp-programming ## Basic Information - **Project Name**: CSharp编程 - **Description**: 实习期间,学习C#加.net学习代码 - **Primary Language**: C# - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2023-02-28 - **Last Updated**: 2024-12-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # C#笔记 1. `• 现代的、通用的编程语言。` 2. `• 面向对象。` 3. `• 面向组件。` 4. `• 容易学习。` 5. `• 结构化语言。` 6. `• 它产生高效率的程序。` 7. `• 它可以在多种计算机平台上编译。` 8. `• .Net 框架的一部分。` ## (一)、数据类型 + 值类型 + 引用类型 + 指针类型 ### 1. 值类型 直接分配一个值,从`System.ValueType` 中派生出来的 + `bool ` 布尔值 True 或 False + byte 8 + char + decimal + double + float + int + long + `sbyte` 8 位有符号整数类型 + short 16 位有符号整数类型 + `uint` + `ulong` + `ushort` > 使用`sizeof()`来判断数据的类型 下表列出了 C# 2010 中可用的值类型: | 类型 | 描述 | 范围 | 默认值 | | :------ | :----------------------------------- | :---------------------------------------------------- | :----- | | bool | 布尔值 | True 或 False | False | | byte | 8 位无符号整数 | 0 到 255 | 0 | | char | 16 位 Unicode 字符 | U +0000 到 U +ffff | '\0' | | decimal | 128 位精确的十进制值,28-29 有效位数 | (-7.9 x 1028 到 7.9 x 1028) / 100 到 28 | 0.0M | | double | 64 位双精度浮点型 | (+/-)5.0 x 10-324 到 (+/-)1.7 x 10308 | 0.0D | | float | 32 位单精度浮点型 | -3.4 x 1038 到 + 3.4 x 1038 | 0.0F | | int | 32 位有符号整数类型 | -2,147,483,648 到 2,147,483,647 | 0 | | long | 64 位有符号整数类型 | -923,372,036,854,775,808 到 9,223,372,036,854,775,807 | 0L | | sbyte | 8 位有符号整数类型 | -128 到 127 | 0 | | short | 16 位有符号整数类型 | -32,768 到 32,767 | 0 | | uint | 32 位无符号整数类型 | 0 到 4,294,967,295 | 0 | | ulong | 64 位无符号整数类型 | 0 到 18,446,744,073,709,551,615 | 0 | | ushort | 16 位无符号整数类型 | 0 到 65,535 | 0 | ### 2. 引用类型 + 对象类型 Object + 动态类型 Dynamic + 字符串类型 String > 例: > > ​ 动态类型 > > ​ dynamic d = 20; > > 字符串类型 > > ​ `String str = "runoob.com";` 或 `@"runoob.com";` ### 3. 指针类型 ### 4. 类型转换 ## (二)、 变量和常量 ## (三)、运算符 + 算术运算符 + 关系运算符 + 逻辑运算符 + 位运算符 + 赋值运算符 + 其他运算符 ### 1. 算术运算符 + + + - + * + / + % + ++ + -- ### 2. 关系运算符 + == + != + < + <= + `>=` ### 3. 逻辑运算符 + && + || + ! ### 4. 位运算符 + & + | + ^ 异或 + ~ 按位取反 + << 二进制位左移 + `>>` 二进制位右移 ### 5. 赋值运算符 + = + += + -= + *= + /= + %= + <<= + `>>=` + &= + ^= + |= ### 6. 其他运算符 + `sizeof()` 返回数据类型的大小 + `typeof()` 返回class的类型 + & 返回变量的地址 + `*` 变量的指针 + ?: 三目运算符 + is 判断对象是否为某一类型 + as 强制类型转换 ## (四)、判断 + if + switch + ? : 三目运算 ## (五)、循环 + for + `foreach` + while + do...while ## (六)、访问修饰符 + public 公有的 所有的对象都能访问 + private 私有的 对象本身在对象内部可以访问 + protected 保护的 只有该类对象及其子类对象可以访问 + internal 内部的 同一个程序集的对象可以访问 + Protected internal 该程序集内的派生类访问,是protected和internal的交集; ## (七)、方法 ``` (Parameter List) { Method Body } 访问修饰符 返回值类型 方法名(参数列表) { 方法体 } ``` + 值传递 + 引用传递 + 在 C# 中,使用 `ref` 关键字声明引用参数 ```c# public void swap(ref int x, ref int y) { int temp; temp = x; /* 保存 x 的值 */ x = y; /* 把 y 赋值给 x */ y = temp; /* 把 temp 赋值给 y */ } ``` + 输出传递 + 在 C# 中,使用 `out` 关键字声明输出传递的参数 ``` public void getValue(out int x ) { int temp = 5; x = temp; } ``` ## (八)、可空类型 ### 1. C# 可空类型(?) C# 提供了一个特殊的数据类型,**`nullable`** 类型(可空类型),可空类型可以表示其基础值类型正常范围内的值,再加上一个 `null` 值。 声明一个 `nullable` 类型(可空类型)的语法如下: ```c# < data_type> ? = null; 数据类型? 变量名 = null; ``` ### 2. Null 合并运算符( ?? ) `Null` 合并运算符用于定义可空类型和引用类型的默认值。 `Null` 合并运算符为类型转换定义了一个预设值,以防可空类型的值为 `Null`。 `Null` 合并运算符把操作数类型隐式转换为另一个可空(或不可空)的值类型的操作数的类型。 > 如果第一个操作数的值为 `null`,则运算符返回第二个操作数的值,否则返回第一个操作数的值。 ```c# using System; namespace CalculatorApplication { class NullablesAtShow { static void Main(string[] args) { double? num1 = null; double? num2 = 3.14157; double num3; num3 = num1 ?? 5.34; Console.WriteLine("num3 的值: {0}", num3); num3 = num2 ?? 5.34; Console.WriteLine("num3 的值: {0}", num3); Console.ReadLine(); } } } num3 的值: 5.34 num3 的值: 3.14157 ``` ## (九)、字符串 `string` 关键字来声明一个字符串变量。`string` 关键字是 `System.String` 类的别名。 ### 1. 创建String对象 - 通过给 `String` 变量指定一个字符串 - `string fname;` - ` fname = "Rowan";` - 通过使用 `String` 类构造函数 - `string greetings = new string(letters);` - 通过使用字符串串联运算符( `+` ) - 通过检索属性或调用一个返回字符串的方法 - `string[] sarray = { "Hello", "From", "Tutorials", "Point" };` - ` string message = String.Join(" ", sarray);` - 通过格式化方法来转换一个值或对象为它的字符串表示形式 - ` DateTime waiting = new DateTime(2012, 10, 10, 17, 58, 1);` - ` string chat = String.Format("Message sent at {0:t} on {0:D}", waiting);` ### 2. String类的属性 | 序号 | 属性名称 & 描述 | | :--- | :-------------------------------------------------- | | 1 | Chars在当前 String 对象中获取 Char 对象的指定位置。 | | 2 | Length在当前的 String 对象中获取字符数。 | ### 3. String类的方法 `String` 类有许多方法用于 `string` 对象的操作。下面的表格提供了一些最常用的方法: | 序号 | 方法名称 & 描述 | | :--- | :----------------------------------------------------------- | | 1 | public static int Compare( string strA, string strB ) 比较两个指定的 string 对象,并返回一个表示它们在排列顺序中相对位置的整数。该方法区分大小写。 | | 2 | public static int Compare( string strA, string strB, bool ignoreCase ) 比较两个指定的 string 对象,并返回一个表示它们在排列顺序中相对位置的整数。但是,如果布尔参数为真时,该方法不区分大小写。 | | 3 | public static string Concat( string str0, string str1 ) 连接两个 string 对象。 | | 4 | public static string Concat( string str0, string str1, string str2 ) 连接三个 string 对象。 | | 5 | public static string Concat( string str0, string str1, string str2, string str3 ) 连接四个 string 对象。 | | 6 | public bool Contains( string value ) 返回一个表示指定 string 对象是否出现在字符串中的值。 | | 7 | public static string Copy( string str ) 创建一个与指定字符串具有相同值的新的 String 对象。 | | 8 | public void CopyTo( int sourceIndex, char[] destination, int destinationIndex, int count ) 从 string 对象的指定位置开始复制指定数量的字符到 Unicode 字符数组中的指定位置。 | | 9 | public bool EndsWith( string value ) 判断 string 对象的结尾是否匹配指定的字符串。 | | 10 | public bool Equals( string value ) 判断当前的 string 对象是否与指定的 string 对象具有相同的值。 | | 11 | public static bool Equals( string a, string b ) 判断两个指定的 string 对象是否具有相同的值。 | | 12 | public static string Format( string format, Object arg0 ) 把指定字符串中一个或多个格式项替换为指定对象的字符串表示形式。 | | 13 | public int IndexOf( char value ) 返回指定 Unicode 字符在当前字符串中第一次出现的索引,索引从 0 开始。 | | 14 | public int IndexOf( string value ) 返回指定字符串在该实例中第一次出现的索引,索引从 0 开始。 | | 15 | public int IndexOf( char value, int startIndex ) 返回指定 Unicode 字符从该字符串中指定字符位置开始搜索第一次出现的索引,索引从 0 开始。 | | 16 | public int IndexOf( string value, int startIndex ) 返回指定字符串从该实例中指定字符位置开始搜索第一次出现的索引,索引从 0 开始。 | | 17 | public int IndexOfAny( char[] anyOf ) 返回某一个指定的 Unicode 字符数组中任意字符在该实例中第一次出现的索引,索引从 0 开始。 | | 18 | public int IndexOfAny( char[] anyOf, int startIndex ) 返回某一个指定的 Unicode 字符数组中任意字符从该实例中指定字符位置开始搜索第一次出现的索引,索引从 0 开始。 | | 19 | public string Insert( int startIndex, string value ) 返回一个新的字符串,其中,指定的字符串被插入在当前 string 对象的指定索引位置。 | | 20 | public static bool IsNullOrEmpty( string value ) 指示指定的字符串是否为 null 或者是否为一个空的字符串。 | | 21 | public static string Join( string separator, string[] value ) 连接一个字符串数组中的所有元素,使用指定的分隔符分隔每个元素。 | | 22 | public static string Join( string separator, string[] value, int startIndex, int count ) 连接接一个字符串数组中的指定位置开始的指定元素,使用指定的分隔符分隔每个元素。 | | 23 | public int LastIndexOf( char value ) 返回指定 Unicode 字符在当前 string 对象中最后一次出现的索引位置,索引从 0 开始。 | | 24 | public int LastIndexOf( string value ) 返回指定字符串在当前 string 对象中最后一次出现的索引位置,索引从 0 开始。 | | 25 | public string Remove( int startIndex ) 移除当前实例中的所有字符,从指定位置开始,一直到最后一个位置为止,并返回字符串。 | | 26 | public string Remove( int startIndex, int count ) 从当前字符串的指定位置开始移除指定数量的字符,并返回字符串。 | | 27 | public string Replace( char oldChar, char newChar ) 把当前 string 对象中,所有指定的 Unicode 字符替换为另一个指定的 Unicode 字符,并返回新的字符串。 | | 28 | public string Replace( string oldValue, string newValue ) 把当前 string 对象中,所有指定的字符串替换为另一个指定的字符串,并返回新的字符串。 | | 29 | public string[] Split( params char[] separator ) 返回一个字符串数组,包含当前的 string 对象中的子字符串,子字符串是使用指定的 Unicode 字符数组中的元素进行分隔的。 | | 30 | public string[] Split( char[] separator, int count ) 返回一个字符串数组,包含当前的 string 对象中的子字符串,子字符串是使用指定的 Unicode 字符数组中的元素进行分隔的。int 参数指定要返回的子字符串的最大数目。 | | 31 | public bool StartsWith( string value ) 判断字符串实例的开头是否匹配指定的字符串。 | | 32 | public char[] ToCharArray()返回一个带有当前 string 对象中所有字符的 Unicode 字符数组。 | | 33 | public char[] ToCharArray( int startIndex, int length ) 返回一个带有当前 string 对象中所有字符的 Unicode 字符数组,从指定的索引开始,直到指定的长度为止。 | | 34 | public string ToLower()把字符串转换为小写并返回。 | | 35 | public string ToUpper()把字符串转换为大写并返回。 | | 36 | public string Trim()移除当前 String 对象中的所有前导空白字符和后置空白字符。 | ``` using System; namespace StringApplication { class StringProg { static void Main(string[] args) { string str1 = "This is test"; string str2 = "This is text"; if (String.Compare(str1, str2) == 0) { Console.WriteLine(str1 + " and " + str2 + " are equal."); } else { Console.WriteLine(str1 + " and " + str2 + " are not equal."); } Console.ReadKey() ; } } } This is test and This is text are not equal. ``` ## (十)、数组 **c#数组是一个存储相同类型元素的固定大小的顺序集合** 在 C# 中声明一个数组,您可以使用下面的语法: ```c# datatype[] arrayName; 数组类型[] 数组名 double[] balance; ``` ### 1. 赋值给数组 ```c# double[] balance = new double[10]; balance[0] = 4500.0; double[] balance = { 2340.0, 4523.69, 3421.0}; int[] marks = new int[5] { 99, 98, 92, 97, 95}; int[] marks = new int[] { 99, 98, 92, 97, 95}; int[] marks = new int[] { 99, 98, 92, 97, 95}; int[] score = marks;//存的是同一个的内存地址 ``` ### 2. 多维数组 ```c# // 二维字符串数组 string [,] names; // 三维数组 int [ , , ] m; int [,] a = new int [3,4] { {0, 1, 2, 3} , /* 初始化索引号为 0 的行 */ {4, 5, 6, 7} , /* 初始化索引号为 1 的行 */ {8, 9, 10, 11} /* 初始化索引号为 2 的行 */ }; ``` ### 3. 交错数组 交错数组是数组的数组。您可以声明一个带有 int 值的交错数组 scores,如下所示: ``` int [][] scores; int[][] scores = new int[2][]{new int[]{92,93,94},new int[]{85,66,87,88}}; ``` ```c# using System; namespace ArrayApplication { class MyArray { static void Main(string[] args) { /* 一个由 5 个整型数组组成的交错数组 */ int[][] a = new int[][]{new int[]{0,0},new int[]{1,2}, new int[]{2,4},new int[]{ 3, 6 }, new int[]{ 4, 8 } }; int i, j; /* 输出数组中每个元素的值 */ for (i = 0; i < 5; i++) { for (j = 0; j < 2; j++) { Console.WriteLine("a[{0}][{1}] = {2}", i, j, a[i][j]); } } Console.ReadKey(); } } } ``` ### 4. 递数组给函数 ``` using System; namespace ArrayApplication { class MyArray { double getAverage(int[] arr, int size) { int i; double avg; int sum = 0; for (i = 0; i < size; ++i) { sum += arr[i]; } avg = (double)sum / size; return avg; } static void Main(string[] args) { MyArray app = new MyArray(); /* 一个带有 5 个元素的 int 数组 */ int [] balance = new int[]{1000, 2, 3, 17, 50}; double avg; /* 传递数组的指针作为参数 */ avg = app.getAverage(balance, 5 ) ; /* 输出返回值 */ Console.WriteLine( "平均值是: {0} ", avg ); Console.ReadKey(); } } } 平均值是: 214.4 ``` ### 5. 参数数组 相当于java中的可变参数(...) 有时,当声明一个方法时,您不能确定要传递给函数作为参数的参数数目。C# 参数数组解决了这个问题,参数数组通常用于传递未知数量的参数给函数。 **`params` 关键字** 在使用数组作为形参时,C# 提供了 `params` 关键字,使调用数组为形参的方法时,既可以传递数组实参,也可以只传递一组数组。`params` 的使用格式为: ``` public 返回类型 方法名称( params 类型名称[] 数组名称 ) ``` 下面的实例演示了如何使用参数数组: ``` using System; namespace ArrayApplication { class ParamArray { // 参数不确定 可变参数 public int AddElements(params int[] arr) { int sum = 0; foreach (int i in arr) { sum += i; } return sum; } } class TestClass { static void Main(string[] args) { ParamArray app = new ParamArray(); int sum = app.AddElements(512, 720, 250, 567, 889); Console.WriteLine("总和是: {0}", sum); Console.ReadKey(); } } } 总和是: 2938 ``` ### 6. Array 类 Array 类是 C# 中所有数组的基类,它是在 System 命名空间中定义。Array 类提供了各种用于数组的属性和方法。 - #### Array类的属性 下表列出了 Array 类中一些最常用的属性: | 序号 | 属性 & 描述 | | :--- | :----------------------------------------------------------- | | 1 | IsFixedSize获取一个值,该值指示数组是否带有固定大小。 | | 2 | IsReadOnly获取一个值,该值指示数组是否只读。 | | 3 | Length获取一个 32 位整数,该值表示所有维度的数组中的元素总数。 | | 4 | LongLength获取一个 64 位整数,该值表示所有维度的数组中的元素总数。 | | 5 | Rank获取数组的秩(维度)。 | - #### Array 类的方法 下表列出了 Array 类中一些最常用的方法: | 序号 | 方法 & 描述 | | :--- | :----------------------------------------------------------- | | 1 | Clear根据元素的类型,设置数组中某个范围的元素为零、为 false 或者为 null。 | | 2 | Copy(Array, Array, Int32)从数组的第一个元素开始复制某个范围的元素到另一个数组的第一个元素位置。长度由一个 32 位整数指定。 | | 3 | CopyTo(Array, Int32)从当前的一维数组中复制所有的元素到一个指定的一维数组的指定索引位置。索引由一个 32 位整数指定。 | | 4 | GetLength 获取一个 32 位整数,该值表示指定维度的数组中的元素总数。 | | 5 | GetLongLength获取一个 64 位整数,该值表示指定维度的数组中的元素总数。 | | 6 | GetLowerBound获取数组中指定维度的下界。 | | 7 | GetType获取当前实例的类型。从对象(Object)继承。 | | 8 | GetUpperBound获取数组中指定维度的上界。 | | 9 | GetValue(Int32)获取一维数组中指定位置的值。索引由一个 32 位整数指定。 | | 10 | IndexOf(Array, Object)搜索指定的对象,返回整个一维数组中第一次出现的索引。 | | 11 | Reverse(Array)逆转整个一维数组中元素的顺序。 | | 12 | SetValue(Object, Int32)给一维数组中指定位置的元素设置值。索引由一个 32 位整数指定。 | | 13 | Sort(Array)使用数组的每个元素的 IComparable 实现来排序整个一维数组中的元素。 | | 14 | ToString返回一个表示当前对象的字符串。从对象(Object)继承。 | ```c# using System; namespace ArrayApplication { class MyArray { static void Main(string[] args) { int[] list = { 34, 72, 13, 44, 25, 30, 10 }; int[] temp = list; Console.Write("原始数组: "); foreach (int i in list) { Console.Write(i + " "); } Console.WriteLine(); // 逆转数组 Array.Reverse(temp); Console.Write("逆转数组: "); foreach (int i in temp) { Console.Write(i + " "); } Console.WriteLine(); // 排序数组 Array.Sort(list); Console.Write("排序数组: "); foreach (int i in list) { Console.Write(i + " "); } Console.WriteLine(); Console.ReadKey(); } } } 原始数组: 34 72 13 44 25 30 10 逆转数组: 10 30 25 44 13 72 34 排序数组: 10 13 25 30 34 44 72 ``` ## (十 一)、结构体 在 C# 中,结构体是值类型数据结构。它使得一个单一变量可以存储各种数据类型的相关数据。**`struct`** 关键字用于创建结构体。 ### 1. 定义结构体 ```c# using System; struct Books { public string title; public string author; public string subject; public int book_id; }; public class testStructure { public static void Main(string[] args) { Books Book1; /* 声明 Book1,类型为 Book */ Books Book2; /* 声明 Book2,类型为 Book */ /* book 1 详述 */ Book1.title = "C Programming"; Book1.author = "Nuha Ali"; Book1.subject = "C Programming Tutorial"; Book1.book_id = 6495407; /* book 2 详述 */ Book2.title = "Telecom Billing"; Book2.author = "Zara Ali"; Book2.subject = "Telecom Billing Tutorial"; Book2.book_id = 6495700; /* 打印 Book1 信息 */ Console.WriteLine( "Book 1 title : {0}", Book1.title); Console.WriteLine("Book 1 author : {0}", Book1.author); Console.WriteLine("Book 1 subject : {0}", Book1.subject); Console.WriteLine("Book 1 book_id :{0}", Book1.book_id); /* 打印 Book2 信息 */ Console.WriteLine("Book 2 title : {0}", Book2.title); Console.WriteLine("Book 2 author : {0}", Book2.author); Console.WriteLine("Book 2 subject : {0}", Book2.subject); Console.WriteLine("Book 2 book_id : {0}", Book2.book_id); Console.ReadKey(); } } Book 1 title : C Programming Book 1 author : Nuha Ali Book 1 subject : C Programming Tutorial Book 1 book_id : 6495407 Book 2 title : Telecom Billing Book 2 author : Zara Ali Book 2 subject : Telecom Billing Tutorial Book 2 book_id : 6495700 ``` ### 2. C#结构体的特点 - 结构可带有方法、字段、索引、属性、运算符方法和事件。 - 结构可定义构造函数,但不能定义**析构函数**。但是,您不能为结构定义默认的构造函数。默认的构造函数是自动定义的,且不能被改变。 - 与类不同,结构不能继承其他的结构或类。 - 结构不能作为其他结构或类的基础结构。 - 结构可实现一个或多个接口。 - 结构成员不能指定为 abstract、virtual 或 protected。 - 当您使用 New 操作符创建一个结构对象时,会调用适当的构造函数来创建结构。与类不同,结构可以不使用 New 操作符即可被实例化。 - 如果不使用 New 操作符,只有在所有的字段都被初始化之后,字段才被赋值,对象才被使用 ### 3. 类 vs 结构 + 类是引用类型,结构是值类型。 + 结构不支持继承。 + 结构不能声明默认的构造函数。 ```c# using System; struct Books { // 私有变量 private string title; private string author; private string subject; private int book_id; // 为变量赋值 public void getValues(string t, string a, string s, int id) { title = t; author = a; subject = s; book_id =id; } public void display() { Console.WriteLine("Title : {0}", title); Console.WriteLine("Author : {0}", author); Console.WriteLine("Subject : {0}", subject); Console.WriteLine("Book_id :{0}", book_id); } }; public class testStructure { public static void Main(string[] args) { Books Book1 = new Books(); /* 声明 Book1,类型为 Book */ Books Book2 = new Books(); /* 声明 Book2,类型为 Book */ /* book 1 详述 */ Book1.getValues("C Programming", "Nuha Ali", "C Programming Tutorial",6495407); /* book 2 详述 */ Book2.getValues("Telecom Billing", "Zara Ali", "Telecom Billing Tutorial", 6495700); /* 打印 Book1 信息 */ Book1.display(); /* 打印 Book2 信息 */ Book2.display(); Console.ReadKey(); } } Title : C Programming Author : Nuha Ali Subject : C Programming Tutorial Book_id : 6495407 Title : Telecom Billing Author : Zara Ali Subject : Telecom Billing Tutorial Book_id : 6495700 ``` ## (十 二)、枚举类型 `enum` 枚举是一组命名整型常量。枚举类型是使用 **enum** 关键字声明的。是值类型 不能被继承和传递 ``` enum { enumeration list }; enum Days { Sun, Mon, tue, Wed, thu, Fri, Sat }; ``` ## (十三)、类——class - 访问标识符 指定了对类及其成员的访问规则。如果没有指定,则使用默认的访问标识符。类的默认访问标识符是 internal,成员的默认访问标识符是 private。 - 数据类型 指定了变量的类型,返回类型 指定了返回的方法返回的数据类型。 - 如果要访问类的成员,你要使用点(.)运算符。 - 点运算符链接了对象的名称和成员的名称。 ```c# using System; namespace BoxApplication { class Box { public double length; // 长度 public double breadth; // 宽度 public double height; // 高度 } class Boxtester { static void Main(string[] args) { Box Box1 = new Box(); // 声明 Box1,类型为 Box Box Box2 = new Box(); // 声明 Box2,类型为 Box double volume = 0.0; // 体积 // Box1 详述 Box1.height = 5.0; Box1.length = 6.0; Box1.breadth = 7.0; // Box2 详述 Box2.height = 10.0; Box2.length = 12.0; Box2.breadth = 13.0; // Box1 的体积 volume = Box1.height * Box1.length * Box1.breadth; Console.WriteLine("Box1 的体积: {0}", volume); // Box2 的体积 volume = Box2.height * Box2.length * Box2.breadth; Console.WriteLine("Box2 的体积: {0}", volume); Console.ReadKey(); } } } Box1 的体积: 210 Box2 的体积: 1560 ``` ### 1. C# 中的析构函数 + 类的 **析构函数** 是类的一个特殊的成员函数,当类的对象超出范围时执行。 + 析构函数的名称是在类的名称前加上一个波浪形(~)作为前缀,它不返回值,也不带任何参数。 + 析构函数用于在结束程序(比如关闭文件、释放内存等)之前释放资源。析构函数不能继承或重载。 ``` using System; namespace LineApplication { class Line { private double length; // 线条的长度 public Line() // 构造函数 { Console.WriteLine("对象已创建"); } ~Line() //析构函数 { Console.WriteLine("对象已删除"); } public void setLength( double len ) { length = len; } public double getLength() { return length; } static void Main(string[] args) { Line line = new Line(); // 设置线条长度 line.setLength(6.0); Console.WriteLine("线条的长度: {0}", line.getLength()); } } } 对象已创建 线条的长度: 6 对象已删除 ``` ### 2. C# 类的静态成员 + 我们可以使用 **static** 关键字把类成员定义为静态的。当我们声明一个类成员为静态时,意味着无论有多少个类的对象被创建,只会有一个该静态成员的副本。 + 关键字 **static** 意味着类中只有一个该成员的实例。静态变量用于定义常量,因为它们的值可以通过直接调用类而不需要创建类的实例来获取。静态变量可在成员函数或类的定义外部进行初始化。你也可以在类的定义内部初始化静态变量。 ``` using System; namespace StaticVarApplication { class StaticVar { public static int num; public void count() { num++; } public int getNum() { return num; } } class StaticTester { static void Main(string[] args) { StaticVar s1 = new StaticVar(); StaticVar s2 = new StaticVar(); s1.count(); s1.count(); s1.count(); s2.count(); s2.count(); s2.count(); Console.WriteLine("s1 的变量 num: {0}", s1.getNum()); Console.WriteLine("s2 的变量 num: {0}", s2.getNum()); Console.ReadKey(); } } } s1 的变量 num: 6 s2 的变量 num: 6 ``` ## (十四)、继承 + 继承是面向对象程序设计中最重要的概念之一。继承允许我们根据一个类来定义另一个类,这使得创建和维护应用程序变得更容易。同时也有利于重用代码和节省开发时间。 + 当创建一个类时,程序员不需要完全重新编写新的数据成员和成员函数,只需要设计一个新的类,继承了已有的类的成员即可。这个已有的类被称为的**基类**,这个新的类被称为**派生类**。 + 继承的思想实现了 **属于(IS-A)** 关系。例如,哺乳动物 **属于(IS-A)** 动物,狗 **属于(IS-A)** 哺乳动物,因此狗 **属于(IS-A)** 动物。 + 实现继承和接口继承 + 所有的类继承object(基类) + 父类方法生命为虚方法的时候子类可以重写 `virtual` + 隐藏方法(在子类中用new) + 虚方法和隐藏方法的区别是在使用多态的时候 父类调用的是自己的方法 虚方法是调用子类重写的方法 语法 ``` class { ... } class : { ... } ``` ```c# using System; namespace InheritanceApplication { class Shape { public void setWidth(int w) { width = w; } public void setHeight(int h) { height = h; } protected int width; protected int height; } // 派生类 Rectangle 继承 Shape类 class Rectangle: Shape { public int getArea() { return (width * height); } } class RectangleTester { static void Main(string[] args) { Rectangle Rect = new Rectangle(); Rect.setWidth(5); Rect.setHeight(7); // 打印对象的面积 Console.WriteLine("总面积: {0}", Rect.getArea()); Console.ReadKey(); } } } 总面积: 35 ``` C#不允许多继承,和java一样的如果要实现多继承的话就要去实现接口 ## (十 五)、多态和抽象类 + 函数重载 + 运算符重载 密封类和密码方法 `c#中在方法和类上加上sealed表示这个方法或类不能被重写或` ## (十 六)、运算符重载 ``` public static Box operator+ (Box b, Box c) { Box box = new Box(); box.length = b.length + c.length; box.breadth = b.breadth + c.breadth; box.height = b.height + c.height; return box; } ``` ## (十 七)、接口 接口定义了属性、方法和事件,这些都是接口的成员。接口只包含了成员的声明。成员的定义是派生类的责任。接口提供了派生类应遵循的标准结构。 接口使用 **interface** 关键字声明,它与类的声明类似。接口声明默认是 `public` 的。下面是一个接口声明的实例: ``` interface IMyInterface { void MethodToImplement(); } ``` 以上代码定义了接口 `IMyInterface`。通常接口命令以 **I** 字母开头,这个接口只有一个方法 `MethodToImplement()`,没有参数和返回值,当然我们可以按照需求设置参数和返回值。 **当实现接口的时候要实现接口中所有的方法** 接口可以继承接口 ## (十 八)、C# 命名空间 **命名空间**的设计目的是提供一种让一组名称与其他名称分隔开的方式。在一个命名空间中声明的类的名称与另一个命名空间中声明的相同的类的名称不冲突。 命名空间的定义是以关键字 **namespace** 开始,后跟命名空间的名称,如下所示: ``` namespace namespace_name { // 代码声明 } ``` ### 1. 嵌套命名空间 命名空间可以被嵌套,即您可以在一个命名空间内定义另一个命名空间,如下所示: ```c# using System; using SomeNameSpace; using SomeNameSpace.Nested; namespace SomeNameSpace { public class MyClass { static void Main() { Console.WriteLine("In SomeNameSpace"); Nested.NestedNameSpaceClass.SayHello(); } } // 内嵌命名空间 namespace Nested { public class NestedNameSpaceClass { public static void SayHello() { Console.WriteLine("In Nested"); } } } } In SomeNameSpace In Nested ``` ## (十 九)、异常处理 异常是在程序执行期间出现的问题。C# 中的异常是对程序运行时出现的特殊情况的一种响应,比如尝试除以零。 异常提供了一种把程序控制权从某个部分转移到另一个部分的方式。C# 异常处理时建立在四个关键词之上的:**try**、**catch**、**finally** 和 **throw**。 - try:一个 try 块标识了一个将被激活的特定的异常的代码块。后跟一个或多个 catch 块。 - catch:程序通过异常处理程序捕获异常。catch 关键字表示异常的捕获。 - finally:finally 块用于执行给定的语句,不管异常是否被抛出都会执行。例如,如果您打开一个文件,不管是否出现异常文件都要被关闭。 - throw:当问题出现时,程序抛出一个异常。使用 throw 关键字来完成。 语法 ```c# try { // 引起异常的语句 } catch( ExceptionName e1 ) { // 错误处理代码 } catch( ExceptionName e2 ) { // 错误处理代码 } catch( ExceptionName eN ) { // 错误处理代码 } finally { // 要执行的语句 } ``` ### 1. C# 中的异常类 C# 异常是使用类来表示的。C# 中的异常类主要是直接或间接地派生于 **System.Exception** 类。 **System.ApplicationException** 和 **System.SystemException** 类是派生于 System.Exception 类的异常类。 **System.ApplicationException** 类支持由应用程序生成的异常。所以程序员定义的异常都应派生自该类。 **System.SystemException** 类是所有预定义的系统异常的基类。 下表列出了一些派生自 Sytem.SystemException 类的预定义的异常类: | 异常类 | 描述 | | :-------------------------------- | :--------------------------------------------- | | System.IO.IOException | 处理 I/O 错误。 | | System.IndexOutOfRangeException | 处理当方法指向超出范围的数组索引时生成的错误。 | | System.ArrayTypeMismatchException | 处理当数组类型不匹配时生成的错误。 | | System.NullReferenceException | 处理当依从一个空对象时生成的错误。 | | System.DivideByZeroException | 处理当除以零时生成的错误。 | | System.InvalidCastException | 处理在类型转换期间生成的错误。 | | System.OutOfMemoryException | 处理空闲内存不足生成的错误。 | | System.StackOverflowException | 处理栈溢出生成的错误。 | ### 2. 异常处理 ``` using System; namespace ErrorHandlingApplication { class DivNumbers { int result; DivNumbers() { result = 0; } public void division(int num1, int num2) { try { result = num1 / num2; } catch (DivideByZeroException e) { Console.WriteLine("Exception caught: {0}", e); } finally { Console.WriteLine("Result: {0}", result); } } static void Main(string[] args) { DivNumbers d = new DivNumbers(); d.division(25, 0); Console.ReadKey(); } } } Exception caught: System.DivideByZeroException: Attempted to divide by zero. at ... Result: 0 ``` ### 3. 创建用户自定义异常 可以定义自己的异常。用户自定义的异常类是派生自 **ApplicationException** 类。 ``` using System; namespace UserDefinedException { class TestTemperature { static void Main(string[] args) { Temperature temp = new Temperature(); try { temp.showTemp(); } catch(TempIsZeroException e) { Console.WriteLine("TempIsZeroException: {0}", e.Message); } Console.ReadKey(); } } } // 自定义异常类 public class TempIsZeroException: ApplicationException { public TempIsZeroException(string message): base(message) { } } public class Temperature { int temperature = 0; public void showTemp() { if(temperature == 0) { throw (new TempIsZeroException("Zero Temperature found")); } else { Console.WriteLine("Temperature: {0}", temperature); } } } TempIsZeroException: Zero Temperature found ``` ### 4. 抛出对象 如果异常是直接或间接派生自 **System.Exception** 类,您可以抛出一个对象。您可以在 catch 块中使用 throw 语句来抛出当前的对象,如下所示: ``` Catch(Exception e) { ... Throw e } ``` ## (二 十)、预处理器指令 下表列出了 C# 中可用的预处理器指令: | 预处理器指令 | 描述 | | :----------- | :----------------------------------------------------------- | | #define | 它用于定义一系列成为符号的字符。 | | #undef | 它用于取消定义符号。 | | #if | 它用于测试符号是否为真。 | | #else | 它用于创建复合条件指令,与 #if 一起使用。 | | #elif | 它用于创建复合条件指令。 | | #endif | 指定一个条件指令的结束。 | | #line | 它可以让您修改编译器的行数以及(可选地)输出错误和警告的文件名。 | | #error | 它允许从代码的指定位置生成一个错误。 | | #warning | 它允许从代码的指定位置生成一级警告。 | | #region | 它可以让您在使用 Visual Studio Code Editor 的大纲特性时,指定一个可展开或折叠的代码块。 | | #endregion | 它标识着 #region 块的结束。 | ## (二 十 一)、文件处理 一个 **文件** 是一个存储在磁盘中带有指定名称和目录路径的数据集合。当打开文件进行读写时,它变成一个 **流**。 从根本上说,流是通过通信路径传递的字节序列。有两个主要的流:**输入流** 和 **输出流**。 **输入流**用于从文件读取数据(读操作),**输出流**用于向文件写入数据(写操作)。 **Path类**提供一些静态方法 Path.Combine连接两个路径,不用关心在哪个系统上(Windows或Linux) ## (二 十 二)、委托 C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针。**委托(Delegate)** 是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。 委托(Delegate)特别用于实现事件和回调方法。所有的委托(Delegate)都派生自 **System.Delegate** 类。 + 是一种存储函数引用的类型 + 指定了一个返回类型和一个参数列表 ``` internal class Program { static void Main(string[] args) { Console.WriteLine(Multiply(1,2)); Console.WriteLine(Divide(1,2)); // 定义委托 Mydelegate mydelegate; // 指向 乘法 mydelegate = Multiply; Console.WriteLine(mydelegate(1, 2)); } // 函数 static double Multiply(double a, double b) { return a * b; } static double Divide(double a, double b) { return a / b; } // 委托 delegate double Mydelegate(double x,double y); } ``` + Action委托 + 返回值位void 任意个参数 + Func委托 + 有返回值 0-16个参数 ## (二 十 三)、属性 ```c# using System; namespace _018.属性 { class Customer { // 数据成员 private string name; private int age; // private string address; private string createTime; //public void SetAge(int age) //{ // this.age = age; //} //public int GetAge() { return this.age; } // 属性访问 public int Age { get { return age; } set // value参数 { if (value < 0) { return; } age = value; } } // 设置属性只读 public int name { get { return age; } } // 简写 public string Name { get; set; } // 自动创建 private string address;成员 public string Address { get;set; } public void show() { Console.WriteLine("名字:" + name); Console.WriteLine("年龄:" + age); // 因为是自动生成的类型所有要和属性保存一致 Console.WriteLine("地址:" + Address); Console.WriteLine("创建时间:" + createTime); } } } ``` 匿名类型 在声明变量的时候不指定类型 var name; ## (二 十 四)、索引器 **索引器(Indexer)** 允许一个对象可以像数组一样被索引。当您为类定义一个索引器时,该类的行为就会像一个 **虚拟数组(virtual array)** 一样。您可以使用数组访问运算符([ ])来访问该类的实例。 ``` element-type this[int index] { // get 访问器 get { // 返回 index 指定的值 } // set 访问器 set { // 设置 index 指定的值 } } ``` ## (二 十 五)、集合 集合(Collection)类是专门用于数据存储和检索的类。这些类提供了对栈(stack)、队列(queue)、列表(list)和哈希表(hash table)的支持。大多数集合类实现了相同的接口。 集合(Collection)类服务于不同的目的,如为元素动态分配内存,基于索引访问列表项等等。这些类创建 Object 类的对象的集合。在 C# 中,Object 类是所有数据类型的基类。 下面是各种常用的 System.Collection 命名空间的类。点击下面的链接查看细节。 | 类 | 描述和用法 | | :--------------------: | :----------------------------------------------------------- | | 动态数组(ArrayList) | 它代表了可被单独索引的对象的有序集合。它基本上可以替代一个数组。但是,与数组不同的是,您可以使用索引在指定的位置添加和移除项目,动态数组会自动重新调整它的大小。它也允许在列表中进行动态内存分配、增加、搜索、排序各项。 | | 哈希表(Hashtable) | 它使用键来访问集合中的元素。当您使用键访问元素时,则使用哈希表,而且您可以识别一个有用的键值。哈希表中的每一项都有一个键/值对。键用于访问集合中的项目。 | | 排序列表(SortedList) | 它可以使用键和索引来访问列表中的项。排序列表是数组和哈希表的组合。它包含一个可使用键或索引访问各项的列表。如果您使用索引访问各项,则它是一个动态数组(ArrayList),如果您使用键访问各项,则它是一个哈希表(Hashtable)。集合中的各项总是按键值排序。 | | 堆栈(Stack) | 它代表了一个后进先出的对象集合。当您需要对各项进行后进先出的访问时,则使用堆栈。当您在列表中添加一项,称为推入元素,当您从列表中移除一项时,称为弹出元素。 | | 队列(Queue) | 它代表了一个先进先出的对象集合。当您需要对各项进行先进先出的访问时,则使用队列。当您在列表中添加一项,称为入队,当您从列表中移除一项时,称为出队。 | | 点阵列(BitArray) | 它代表了一个使用值 1 和 0 来表示的二进制数组。当您需要存储位,但是事先不知道位数时,则使用点阵列。您可以使用整型索引从点阵列集合中访问各项,索引从零开始。 | ## (二 十 六)、正则表达式 ``` using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace _036.正则表达式 { internal class Program { static void Main(string[] args) { // \d 数字 \D 补集 // \w 大写字母 0-9 _ \W 补集 // 全部都是数字 //Console.WriteLine(Regex.IsMatch("12545",@"^\d*$")); // 判断是否是合法标识符 //Console.WriteLine(Regex.IsMatch("sada8", @"^[a-zA-Z_]\w*$")); // qq号 5-12位数字 //Console.WriteLine(Regex.IsMatch("122222", @"^\d{5,12}$")); // 择一匹配 or Console.WriteLine(Regex.IsMatch("4", @"\d|a")); Console.ReadKey(); } } } ``` ## (二 十 七)、特性 ``` //#define IsShowMessgae using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; namespace _40.特性 { internal class Program { [Obsolete("这个方法弃用了,请使用最新的NewTest方法", false)] // 特性: 弃用 就不能调用了 true表示不能调用会报错 默认是 false static void Test() { Console.WriteLine("test"); } static void NewTest() { Console.WriteLine("NewTest"); } [Conditional("IsShowMessgae")] // 当 #define IsShowMessgae 被定义时 方法会被调用 否则不调用 static void ShowMessgae(string str) { Console.WriteLine(str); } // 调用者信息特性 [CallerLineNumber] int linNumber = 0 多少行 // [CallerFilePath]string filePath="" 调用者的文件路径 // [CallerMemberName] string memberName = "" 那个方法调用 [DebuggerStepThrough] // 当debug调用时会跳过次方法 static void ShowMessgae2(string str, [CallerLineNumber] int linNumber = 0, [CallerFilePath] string filePath = "", [CallerMemberName] string memberName = "") { Console.WriteLine(str); Console.WriteLine(linNumber); Console.WriteLine(filePath); Console.WriteLine(memberName); } static void Main(string[] args) { //Test(); ShowMessgae("Strat of Main"); Console.WriteLine("Doing work in Main"); ShowMessgae("End of Main"); Console.WriteLine("-----------"); ShowMessgae2("hello"); } } } ``` ## (二 十 八)、网络编程 ## (二 十 九)、XML 可扩展标记语言 ``` using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Xml; using System.Xml.Linq; namespace _056.XML操作 { internal class Program { static void Main(string[] args) { List skills = new List(); // 解析XML文档 XmlDocument xmlDoc = new XmlDocument(); // 加载 路径的形式 //xmlDoc.Load("Skills.xml"); // 字符串的形式 xmlDoc.LoadXml(File.ReadAllText("Skills.xml")); // FirstChild 是文档的声明 //XmlNode root = xmlDoc.FirstChild; XmlNode root = xmlDoc.ChildNodes[1]; XmlNodeList skillList = root.ChildNodes; //遍历 foreach (XmlNode skill in skillList) { Skill skillObj = new Skill(); // 遍历的方式 //XmlNodeList list = skill.ChildNodes; //foreach (XmlNode node in list) //{ // //Console.WriteLine(node.Name+ ":"+ node.InnerText); // if (node.Name == "id") // { // skillObj.ID = Int32.Parse(node.InnerText); // } // else if (node.Name == "name") // { // skillObj.Name = node.InnerText; // skillObj.Lang = node.Attributes[0].Value; // } // else // { // skillObj.Damage = Int32.Parse(node.InnerText); // } //} // 索引器的方式 XmlElement idEle = skill["id"]; skillObj.ID = Int32.Parse(idEle.InnerText); XmlElement nameEle = skill["name"]; skillObj.Name = nameEle.InnerText; XmlAttributeCollection attrCol = nameEle.Attributes; XmlAttribute attri= attrCol["lang"]; skillObj.Lang = attri.Value; XmlElement damageEle = skill["damage"]; skillObj.Damage = Int32.Parse(damageEle.InnerText); skills.Add(skillObj); } foreach (Skill skill in skills) { Console.WriteLine(skill.ID + "," + skill.Name + "," + skill.Lang + "," + skill.Damage); } } } } using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _056.XML操作 { class Skill { public int ID { get; set; } public string Name { get; set; } public string Lang { get; set; } public int Damage { get; set; } } } ``` ```xml 2 痴情 288 3 决绝 500 4 虚空 100 ``` ## (三 十)、JSON + 是轻量级的文本数据交换格式 + 独立语言 * + 具有自我描述,更易于理解 语法规则 + 数据在键值对中 + 由逗号分割 + 花括号保存在对象 + 方括号保存数组 ```c# using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _057.Json操作 { internal class Program { static void Main(string[] args) { // Serialize 反序列化 把字符串转换为对象 //Skill[] skillArray = JsonConvert.DeserializeObject(File.ReadAllText("Skills.txt")); //foreach (Skill skill in skillArray) //{ // Console.WriteLine(skill.id + "," + skill.name + "," + skill.damage); //} // 序列化 Skill skill = new Skill(); skill.id = 1; skill.name = "Test"; skill.damage = 100; string str = JsonConvert.SerializeObject(skill); Console.WriteLine(str); } } } using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _057.Json操作 { class Skill { // 名字要与json格式保持一致 public int id { get; set; } public string name { get; set; } public int damage { get; set; } } } ``` ```txt [ {"id":2,"name":"痴情","damage":200}, {"id":3,"name":"决绝","damage":255}, {"id":4,"name":"大杀四方","damage":300} ] ``` ## (三 十 一)、操作Excel(OLEDB) ``` using System; using System.Collections.Generic; using System.Data; using System.Data.OleDb; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _058.Excel操作 { internal class Program { static void Main(string[] args) { // 文件名 string fileName = "xxx.xls"; string connectionStr = "Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=" + fileName + ";" + ";Extended Properties=\"Excel 8.0;HDR=YES;IME=1\""; OleDbConnection oleDbConnection = new OleDbConnection(connectionStr); oleDbConnection.Open(); string sql = "select * from [Sheet1$]"; // 数据处理 OleDbDataAdapter oleDbDataAdapter = new OleDbDataAdapter(sql, connectionStr); // 数据集合 DataSet excelData = new DataSet(); // 添加到DataSet中 oleDbDataAdapter.Fill(excelData); oleDbConnection.Close(); // 表格集合 DataTableCollection tableCollection = excelData.Tables; DataTable table = tableCollection[0]; DataRowCollection rowCollection = table.Rows; // 遍历行 foreach(DataRow row in rowCollection) { for(int i = 0; i < 8; i++) { Console.Write(row[i]+" "); } Console.WriteLine(); } } } } ``` ## (三 十 二)、LINQ ``` using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _059.LINQ { internal class Program { static void Main(string[] args) { // 武林高手 var masterList = new List() { new MartialArtsMaster() {Id = 100,Name="张三",Age = 100,Kongfu = "葵花宝典",Level=9}, new MartialArtsMaster() {Id = 200,Name="张三2",Age = 100,Kongfu = "葵花宝典2",Level=9}, new MartialArtsMaster() {Id = 300,Name="张三3",Age = 100,Kongfu = "打狗棒法",Level=9}, new MartialArtsMaster() {Id = 400,Name="张三4",Age = 100,Kongfu = "葵花宝典4",Level=9}, new MartialArtsMaster() {Id = 500,Name="张三5",Age = 100,Kongfu = "打狗棒法",Level=9} }; // 武学 var kongfuList = new List { new Kongfu() {Id=1,Name="打狗棒法",Power=90}, new Kongfu() {Id=2,Name="打狗棒法1",Power=95}, new Kongfu() {Id=3,Name="打狗棒法2",Power=100}, new Kongfu() {Id=4,Name="打狗棒法3",Power=80}, }; // 查询所有级别大于8的 //var res = new List(); //foreach (var temp in masterList) //{ // if (temp.Level > 8) // { // res.Add(temp); // } //} // 使用LINQ查询 (表达式写法) //var res = from m in masterList where m.Level > 8 && m.Age == 100 select m; // 委托类型 //var res = masterList.Where(Test1); // lamda var res = masterList.Where(m => m.Level > 8 && m.Age == 100); foreach (var item in res) { Console.WriteLine(item); } Console.WriteLine("--------------------"); // 联合查询 //var res1 = from m in masterList // from k in kongfuList // where m.Kongfu == k.Name && k.Power >=90 // select new {master = m,kongfu = k}; // 扩展方法 var res1 = masterList.SelectMany(m => kongfuList, (m, k) => new { master = m, kongfu = k }) .Where(x => x.master.Kongfu == x.kongfu.Name && x.kongfu.Power >= 90); foreach (var item in res1) { Console.WriteLine(item); } Console.WriteLine("--------------------"); // 排序 //var res2 = from m in masterList // where m.Level > 8 && m.Age == 100 // orderby m.Id descending // select m; var res2 = masterList.Where(m => m.Level > 8 && m.Age == 100) .OrderByDescending(m => m.Id).ThenBy(m => m.Age);// ThenBy ID相同再根据年龄排序 foreach (var item in res2) { Console.WriteLine(item); } Console.WriteLine("--------------"); // 集合联合查询 join on var res3 = from m in masterList join k in kongfuList on m.Kongfu equals k.Name where k.Power >= 90 select new { master = m, kongfu = k }; foreach (var item in res3) { Console.WriteLine(item); } Console.WriteLine("--------------"); // 分组查询 into groups var res4 = from k in kongfuList join m in masterList on k.Name equals m.Kongfu into groups orderby groups.Count() descending select new {kongfu = k,count = groups.Count() }; foreach (var item in res4) { Console.WriteLine(item); } Console.WriteLine("--------------"); // 按照自身字段进行分组 group by var res5 = from m in masterList group m by m.Kongfu into g select new {count=g.Count(),g.Key};// key表示按照那个属性分的组 foreach (var item in res5) { Console.WriteLine(item); } Console.WriteLine("--------------"); // 量词操作符 // any 是否有一个有 bool r = masterList.Any(m => m.Menpai == "丐版"); Console.WriteLine(r); // 是否都属于 all masterList.All(m => m.Menpai == "丐版"); Console.WriteLine(r); } // 过滤方法 static bool Test1(MartialArtsMaster master) { if (master.Level > 8) { return true; } return false; } } } ```