目录
[1. 实现通用打印泛型类,可以打印各个集合中的值,方便调试](#1. 实现通用打印泛型类,可以打印各个集合中的值,方便调试)
[2. 计算遍历目录的耗时](#2. 计算遍历目录的耗时)
[3. 有哪些算术运算符,有哪些关系运算符,有哪些逻辑运算符,有哪些位运算符,有哪些赋值运算符](#3. 有哪些算术运算符,有哪些关系运算符,有哪些逻辑运算符,有哪些位运算符,有哪些赋值运算符)
[4. 三目表达式举例](#4. 三目表达式举例)
[5. 优先级口诀:有括号先括号,后乘除再加减,然后位移再关系,逻辑完后再条件](#5. 优先级口诀:有括号先括号,后乘除再加减,然后位移再关系,逻辑完后再条件)
[6. 写个例子展示break、continue、ruturn的区别](#6. 写个例子展示break、continue、ruturn的区别)
[7. 写个冒泡排序](#7. 写个冒泡排序)
[8. 写个九九乘法表](#8. 写个九九乘法表)
[9. 实现文件找不到抛出异常](#9. 实现文件找不到抛出异常)
1. 实现通用打印泛型类,可以打印各个集合中的值,方便调试
在C#中,实现一个通用的打印泛型类,用于打印各种集合中的值,是一个很好的调试辅助工具。我在
DJUtils
类中定义了一个静态泛型方法println<T>
,该方法旨在遍历并打印实现了IEnumerable<T>
接口的集合中的每个元素。
println<T>
是一个泛型方法,它使用泛型类型参数T
。这意味着这个方法可以处理任何实现了IEnumerable<T>
接口的集合,无论集合中元素的类型是什么。- 方法接受一个类型为
IEnumerable<T>
的参数Collection
。IEnumerable<T>
是一个接口,表示一个可以逐个遍历其成员的集合。通过实现这个接口,集合类型(如List<T>、Array等)提供了遍历其元素的能力。- 方法体内部,使用了一个
foreach
循环来遍历集合中的每个元素。对于集合中的每个元素(类型为T
),它都会将该元素转换为字符串(通过item + " "
),并写入到控制台DJUtils.cs
csnamespace DJConsoleProject { //自定义工具类 class DJUtils { //通用的打印 //必须继承IEnumerable接口的类,满足它才能进行迭代 //泛型方法 static public void println<T>(IEnumerable<T> Collection) { foreach (T item in Collection) { Console.Write(item + " "); } Console.WriteLine(); } } }
Program.cs
cs//动态数组 List<string> nameList = new List<string>(); nameList.Add("陈晨"); nameList.Add("陈川"); DJUtils.println(nameList); //链表 LinkedList<string> list = new LinkedList<string>(); list.AddFirst("刘备"); list.AddLast("关羽"); list.AddLast("张飞"); DJUtils.println(list);
2. 计算遍历目录的耗时
这个使用了
Stopwatch
类来测量时间,这是.NET中用于高精度时间测量的类。在遍历开始前,我们启动了
Stopwatch
实例来记录时间。遍历完成后,我们停止Stopwatch
并输出遍历整个目录结构所花费的时间(以毫秒为单位)。
csclass Program { static void Main(string[] args) { string directoryPath = @"C:\Users\Acer\source\repos\DJConsoleProject"; Stopwatch sw = Stopwatch.StartNew(); sw.Start(); // 调用递归函数开始遍历 TraverseDirectory(directoryPath); sw.Stop(); Console.ReadKey(); Console.WriteLine("总耗时:{0}毫秒",sw.ElapsedMilliseconds); } // 递归遍历目录 static void TraverseDirectory(string path) { try { // 获取指定目录下的所有文件和子目录 string[] files = Directory.GetFiles(path); string[] dirs = Directory.GetDirectories(path); // 遍历文件 foreach (var file in files) { FileInfo fileInfo = new FileInfo(file); Console.WriteLine($"文件名: {fileInfo.Name}, 扩展名: {fileInfo.Extension}, 文件大小: {fileInfo.Length} 字节"); } // 遍历子目录 foreach (var dir in dirs) { Console.WriteLine($"进入目录: {dir}"); TraverseDirectory(dir); // 递归调用 Console.WriteLine($"离开目录: {dir}"); } } catch (UnauthorizedAccessException) { Console.WriteLine($"无法访问目录 {path},权限不足。"); } catch (Exception ex) { Console.WriteLine($"遍历目录时发生错误: {ex.Message}"); } } }
结果如图:
3. 有哪些算术运算符,有哪些关系运算符,有哪些逻辑运算符,有哪些位运算符,有哪些赋值运算符
1)算术运算符
算术运算符用于执行基本的数学运算,如加、减、乘、除等。
运算符 描述 示例 + 加法 int a = 5; int b = 3; int c = a + b; // c = 8
- 减法 int a = 5; int b = 3; int c = a - b; // c = 2
* 乘法 int a = 5; int b = 3; int c = a * b; // c = 15
/ 除法 int a = 10; int b = 2; int c = a / b; // c = 5
% 取余 int a = 10; int b = 3; int c = a % b; // c = 1
2)关系运算符
关系运算符用于比较两个值,并返回一个布尔值(
true
或false
)。
运算符 描述 示例 == 等于 int a = 10; int b = 10; bool c = a == b; // c = true
!= 不等于 int a = 10; int b = 5; bool c = a != b; // c = true
< 小于 int a = 5; int b = 10; bool c = a < b; // c = true
<= 小于或等于 int a = 5; int b = 5; bool c = a <= b; // c = true
> 大于 int a = 10; int b = 5; bool c = a > b; // c = true
>= 大于或等于 int a = 10; int b = 10; bool c = a >= b; // c = true
3)逻辑运算符
逻辑运算符用于根据一个或多个条件的布尔值来执行复杂的逻辑运算。
运算符 描述 示例 && 逻辑与 bool a = true; bool b = false; bool c = a && b; // c = false
|| 逻辑或 `bool a = true; bool b = false; bool c = a ! 逻辑非 bool a = false; bool b = !a; // b = true
cs//逻辑运算符 //逻辑与,当两个条件都为真,结果为真 Console.WriteLine(true && true); Console.WriteLine(true && false); Console.WriteLine(false && true); Console.WriteLine(false && false); Console.WriteLine(); //逻辑或,有一个条件为真,结果为真 Console.WriteLine(true || true); Console.WriteLine(true || false); Console.WriteLine(false || true); Console.WriteLine(false || false);
4)位运算符
位运算符在整数类型上操作,按位执行运算。
运算符 描述 示例 ~ 位逻辑非 int a = 5; int b = ~a; // b = -6
& 位逻辑与 int a = 5; int b = 3; int c = a & b; // c = 1
| 位逻辑或 `int a = 5; int b = 3; int c = a ^ 位逻辑异或 int a = 5; int b = 3; int c = a ^ b; // c = 6
<< 位左移 int a = 5; int b = a << 2; // b = 20
>> 位右移 int a = 8; int b = a >> 2; // b = 2
cs//左移性能高,左移一位,等价*2 int num = 1; int times = 10000*10000; //编译器优化,编译后直接使用计算结果 //打印执行的时间,stopwatch Stopwatch sw = new Stopwatch(); sw.Start(); //计时开始 for (int i = 0; i < times; i++) { num *= 2; } sw.Stop(); //计时结束 Console.WriteLine("耗时:{0}毫秒",sw.ElapsedMilliseconds); sw.Restart(); //重置 sw.Start (); for (int i = 0; i < times; i++) { num <<= 1; //左移一位,等价*2 } sw.Stop (); Console.WriteLine("耗时:{0}毫秒", sw.ElapsedMilliseconds);
5)赋值运算符
赋值运算符用于给变量赋值。C# 还支持复合赋值运算符,它们结合了算术、位或逻辑运算符与赋值操作。
运算符 描述 示例 = 赋值 int a; a = 5; // a = 5
+= 加后赋值 int a = 5; a += 3; // a = 8
-= 减后赋值 int a = 5; a -= 3; // a = 2
*= 乘后赋值 int a = 5; a *= 3;
4. 三目表达式举例
C# 中的三目表达式(也称为条件运算符)是一种简洁的编写条件判断语句的方式。它的基本形式是
条件 ? 表达式1 : 表达式2
。如果条件为真(非零或非空),则计算并返回表达式1的结果;如果条件为假(零或空),则计算并返回表达式2的结果。我们写一个三目表达式用于比较两个整数
a
和b
的大小,并将较大的数赋值给变量max:
csclass Program { static void Main() { int a = 10; int b = 20; // 使用三目表达式比较a和b,将较大的值赋给变量max int max = a > b ? a : b; Console.WriteLine($"较大的数是: {max}"); } }
5. 优先级口诀:有括号先括号,后乘除再加减,然后位移再关系,逻辑完后再条件
在C#(以及大多数编程语言)中,运算符的优先级遵循一定的规则,这些规则通常基于数学和逻辑运算的常规顺序。
下面是一个简化的表格,展示了C#中一些运算符的优先级(从高到低):
优先级 运算符类别 示例 1 括号 (a + b) * c
2 成员访问、数组索引、方法调用、后置递增/递减 obj.Method()
,arr[index]
,x++
,x--
3 前置递增/递减、一元运算符(包括逻辑非 !
、按位非~
、正号+
、负号-
等)++x
,--x
,!flag
,~bitMask
,+value
,-value
4 乘除、取余、类型转换 a * b
,a / b
,a % b
,(int)value
5 加减、字符串连接 a + b
,"Hello " + "World"
6 位移 a << b
,a >> b
7 关系比较 a > b
,a < b
,a == b
,a != b
8 等号运算符 a = b
9 位逻辑与 a & b
10 位逻辑异或 a ^ b
11 位逻辑或 `a 12 逻辑与 a && b
13 逻辑或 `a 14 条件运算符 a ? b : c
15 赋值运算符 a = b
,a += b
此外,当运算符具有相同优先级时,它们的操作顺序是从左到右(除了赋值运算符,它是从右到左的,但通常与表达式中的其他元素一起评估时,这种差异不会直接影响结果)。在存在疑问的情况下,可以使用括号来明确指定运算的顺序。
6. 写个例子展示break、continue、ruturn的区别
在C#中,
break
、continue
和return
是控制流语句,用于在循环或方法体中改变正常的执行流程。
break :用于立即退出包含它的最内层循环或
switch
语句。continue:用于跳过当前迭代中剩余的语句,并继续下一次迭代(如果有的话)。
return :用于从当前方法中返回,并将控制权交还给方法的调用者。
return
可以返回一个值给调用者(如果方法签名声明了返回类型),或者在没有返回值的情况下(如void
方法)用于退出方法。下面通过一个例子来展示它们之间的区别。
csusing System; class Program { static void Main(string[] args) { // 演示break、continue和return Console.WriteLine("演示break、continue和return:"); // 假设我们要遍历一个数组,并根据条件执行不同的控制流语句 int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; // 使用for循环遍历数组 for (int i = 0; i < numbers.Length; i++) { if (numbers[i] == 5) { // 当元素为5时,使用break退出循环 Console.WriteLine($"遇到5,使用break退出循环。当前元素:{numbers[i]}"); break; } else if (numbers[i] % 2 == 0) { // 当元素为偶数时,使用continue跳过当前迭代 Console.WriteLine($"跳过偶数:{numbers[i]}"); continue; } // 否则,打印当前元素 Console.WriteLine($"处理元素:{numbers[i]}"); // 注意:这里不会执行到return,因为return用于从方法中返回 // 但为了演示,我们可以假设有一个条件会在某个点触发return if (numbers[i] == 10) { // 在Main中不能直接return,除非返回类型为void或特定类型并返回相应值 // return; // 但为了说明return的概念,我们可以调用一个返回的方法 ReturnDemo(); } } // 演示从方法中返回 Console.WriteLine("调用ReturnDemo()后继续执行。"); } // 一个演示return的方法 static void ReturnDemo() { // 注意:由于这个方法是void类型,所以我们只能使用return来退出方法,而不返回任何值 Console.WriteLine("从ReturnDemo()中返回,但不退出Main方法。"); // return; // 实际上这个return是隐式的,因为方法已经执行完毕 } }
7. 写个冒泡排序
冒泡排序是一种简单的排序算法,它重复地遍历要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。遍历数列的工作是重复进行的,直到没有再需要交换的元素为止,这意味着数列已经排序完成。
冒泡排序的时间复杂度:O(n^2),其中n是数组的长度
冒泡排序的空间复杂度:O(1)
进行两层嵌套的循环遍历数组,外层循环控制排序的总轮数,内层循环负责在每一轮中进行实际的元素比较和交换。
csusing System; class Program { static void Main(string[] args) { int[] arr = { 64, 34, 25, 12, 22, 11, 90 }; int n = arr.Length; Console.WriteLine("原始数组:"); PrintArray(arr); BubbleSort(arr); Console.WriteLine("\n排序后的数组:"); PrintArray(arr); } // 冒泡排序函数 static void BubbleSort(int[] arr) { int n = arr.Length; for (int i = 0; i < n-1; i++) { for (int j = 0; j < n-i-1; j++) { if (arr[j] > arr[j+1]) { // 交换 arr[j] 和 arr[j+1] int temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } } // 打印数组函数 static void PrintArray(int[] arr) { foreach (int num in arr) { Console.Write(num + " "); } Console.WriteLine(); } }
结果如图:
8. 写个九九乘法表
写九九乘法表,我们使用了两个
for
循环:
- 外层循环(由变量
i
控制)遍历从1到9的每个数字,代表乘法表的每一行。- 内层循环(由变量
j
控制)遍历从1到当前外层循环的索引i
的每个数字,代表当前行中的每一列。这样做可以确保每一行的列数与行号相匹配(即第一行有1列,第二行有2列,依此类推)。
csstatic void Main() { for (int i = 1; i <= 9; i++) { for (int j = 1; j <= i; j++) { int a = i * j; //Console.Write("{0} * {1} = {2}\t", i, j, a); Console.Write($"{i} * {j} = {i * j} "); } Console.WriteLine(); } }
结果如图:
9. 实现文件找不到抛出异常
在C#中,当访问到一个不存在的文件时,并不需要手动"抛出"
FileNotFoundException
异常,因为这个异常是由.NET Framework在底层自动抛出的。但是,可以通过编写代码来"触发"这个异常的发生,并在上层捕获并处理它。我们使用
File.Open
方法打开一个指定的文件。由于这个文件不存在,因此File.Open
将抛出FileNotFoundException
异常。我们通过try-catch
块来捕获这个异常,并在捕获到它时打印出一条错误消息。这样,即使文件不存在,我们的程序也不会崩溃,而是会优雅地处理这个错误情况。
csclass Program { static void Main(string[] args) { // 假设我们要访问的文件路径 string filePath = @"C:\ddd.txt"; try { // 尝试打开文件 using (FileStream fs = File.Open(filePath, FileMode.Open)) { Console.WriteLine("文件成功打开"); } } catch (FileNotFoundException ex) { // 如果文件不存在,将捕获FileNotFoundException异常 Console.WriteLine($"文件未找到:{ex.Message}"); // 这里可以添加更多处理异常的逻辑 } catch (Exception ex) { // 捕获其他类型的异常 Console.WriteLine($"发生错误:{ex.Message}"); } Console.ReadKey(); } }
结果如图: