项目基本结构
//引入命名空间 //命名空间:看做是一个范围,用来标注一些成员的归属 //using 关键字 用来引入指定的命名空间,它可以告诉编译器,我们后续的代码中,使用的是哪个命名空间 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; //namespace 命名空间的名称 定义一个命名空间 namespace _01_项目基本结构 { //internal 访问修饰符,设置类能被访问的范围 //class 创建一个类, 类名是"Program" internal class Program { //函数(方法) 名字为Main Main函数/Main方法 是程序的主函数 //这个名字为Main的函数,是整个程序的入口,每一个程序都至少有一个Main函数,所有的代码都是从Main函数开始执行的 //格式:static void 方法名(string[] args){} 创建一个函数 //static:将Main方法声明为静态方法 //void:函数的返回值类型 void 空返回 函数执行完毕之后不会返回任何内容 //string[] args : 参数的数据类型 string 字符串 string[] string 类型的数组 声明了一个数据类型为字符串数组的变量args static void Main(string[] args) { //控制台输出一段内容 //代码-->控制台 Console.WriteLine("吴亦凡"); /* * * */ //控制台输入一段内容 //控制台---> 代码 //等待用户交互 //https://learn.microsoft.com/zh-cn/visualstudio/ide/reference/how-to-manage-word-wrap-in-the-editor?view=vs-2022 //工具-->选项-->文本编辑器-->C#-->常规 -->自动换行 //暂且: 之所以写这行代码.是因为命令行窗口会在程序结束后,自动关闭,会导致我们想要输出的内容一闪而过,加入这个代码则可以避免这种启动 Console.ReadKey(); } } //一个命名空间中的类,与另一个命名空间中的类,互不冲突,但是一个命名空间中的类名必须是唯一的 class Student { } } namespace _02_项目基本结构 { class Student { } }
控制台相关常用方法
using System; namespace _02_控制台相关常用方法 { internal class Program { static void Main(string[] args) { Console.WriteLine("控制台输出一段内容"); Console.WriteLine("控制台输出一段内容"); Console.WriteLine("控制台输出一段内容"); Console.WriteLine("控制台输出一段内容"); //通过控制台扬声器播放提示音 //Console.Beep(); //通过控制台扬声器播放具有指定频率和持续时间的提示音 //参数1:提示音的频率,介于 37-32767赫兹之间 //参数2:提示音的持续时间 以毫秒为单位 Console.Beep(2000,1000); //清除控制台缓存区的内容 Console.Clear(); //获取用户输入的一个字符,或者功能键, 如果用户没有输入字符,后续的代码就不会执行 //Console.ReadKey(); //如果() 中写一个true,表示不在控制台显示按下的字符 //Console.ReadKey(true); //Console.WriteLine("用户按下了一个键"); //获取用户输入的一行文本(输入之后敲回车,才算输入一行内容) //Console.ReadLine(); //Console.WriteLine("用户输入了一行内容,并且按下了回车"); //输出一段内容并换行 Console.WriteLine("输出内容"); Console.WriteLine("输出内容"); //输入一段内容(不会换行) \n表示换行 Console.Write("输出内容\n"); Console.Write("输出内容"); //总结:以上这些操作,后面都带有一个(),我们将这些操作称之为方法的调用 //也就是说,方法就是通过()让它执行的 //属性:记录一些信息 //通过.进行获取 通过=进行设置 Console.WriteLine("获取当前控制台的背景颜色"); //用于获取控制台的背景色 Console.WriteLine(Console.BackgroundColor); //用于设置控制台的背景色 Console.BackgroundColor=ConsoleColor.Red; Console.WriteLine("吴亦凡"); Console.WriteLine("吴亦凡"); Console.WriteLine("吴亦凡"); Console.WriteLine("获取当前控制台的前景色(字体颜色)"); //获取字体颜色 Console.WriteLine(Console.ForegroundColor); Console.ForegroundColor=ConsoleColor.Yellow; //总结: /* * 方法:就是行为,用()触发这个行为 xxx.xxx(); * 属性:记录了一些信息,可以使用.进行获取 xxx.xxx * 可以使用=进行设置(修改) xxx.xxxx=xxxx; */ } } }
C#标识符
标识符是用来识别类、变量、函数或任何其它用户定义的项目。(通俗的说就是类名、变量名、函数名等)在 C# 中,命名必须遵循如下基本规则:
-
标识符由字母、数字、下划线(_)组成。
-
标识符必须以字母、下划线或 @ 开头,后面可以跟一系列的字母、数字( 0 - 9 )、下划线( _ )、@。
-
标识符中的第一个字符不能是数字。
-
标识符必须不包含任何嵌入的空格或符号,比如 ? - +! # % ^ & * ( ) [ ] { } . ; : " ’ / \。
-
标识符不能是 C# 关键字。除非它们有一个 @ 前缀。 例如,@if 是有效的标识符,但 if 不是,因为 if 是关键字。
-
标识符必须区分大小写。大写字母和小写字母被认为是不同的字母。
-
不能与C#的类库名称相同。
C#关键字
保留关键字 | ||||||
---|---|---|---|---|---|---|
abstract | as | base | bool | break | byte | case |
catch | char | checked | class | const | continue | decimal |
default | delegate | do | double | else | enum | event |
explicit | extern | false | finally | fixed | float | for |
foreach | goto | if | implicit | in | in (generic modifier) | int |
interface | internal | is | lock | long | namespace | new |
null | object | operator | out | out (generic modifier) | override | params |
private | protected | public | readonly | ref | return | sbyte |
sealed | short | sizeof | stackalloc | static | string | struct |
switch | this | throw | true | try | typeof | uint |
ulong | unchecked | unsafe | ushort | using | virtual | void |
volatile | while |
上下文关键字 | ||||||
---|---|---|---|---|---|---|
add | alias | ascending | descending | dynamic | from | get |
global | group | into | join | let | orderby | partial (type) |
partial (method) | remove | select | set |
变量和常量
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _03_变量与常量 { internal class Program { static void Main(string[] args) { //变量 //变量就是对程序中某个数据的引用,比如: name 表示一个名字, x表示一个数值..... // 为什么要有变量? //1.我们可以用一个简单的单词,表示一个很长的字符,比如:可以使用变量s表示一个身份证号 //2.可以使用一个变量,在不同的位置表示不同的值 //变量在程序中只是一个存储数据的地方,每一个变量都有一个特定的类型,类型决定了变量所占用的内存空间的大小 //变量的定义(声明) //常用的类型:int double bool string char // 格式:数据类型 变量名(自定义,遵循标识符的命名规范+见名知意) int a23_; int @b23_; int @int; int A; string name; int age; //变量的赋值 int i1; //使用=赋值 (= 赋值运算符) //把=右边的值赋值给=左边的变量 i1 = 10; //没有双引号就表示使用该变量存储的值 Console.WriteLine(i1); //有双引号表示输出""里面的内容 Console.WriteLine("i1"); //如果变量被再次赋值,那么将会将之前保存的值丢失,重新保存新的值 i1 = 20; Console.WriteLine(i1); //声明变量并赋值 //可以在定义变量的时候直接给变量赋值 int i2 = 10; Console.WriteLine(i2); // Console.WriteLine(i3); //可以同时定义多个同类型的变量 int i3, i4, i5; //同时定义多个同类型的变量并赋值 int i6 = 100, i7 = 50; //注意: //1.相同名称的变量只能被声明一次 //2.变量在声明之前不能被使用 //可以改变 int a = 10; a = 20; //常量: //常量和变量类似: 唯一不同的是常量的值在程序编辑阶段就已经确定了,而且在程序运行期间不允许修改 //const int b; //b = 30; const int I = 10; //I = 20; //输出小技巧 Console.WriteLine("I的值是:"+I); Console.WriteLine("i3的值是:"+i2); //在第一个位置的""使用{从0开始的数字} 来表示后续传入的数据 Console.WriteLine("i2的值为:{0}",i2); Console.WriteLine("i1的值为:{0},i2的值为:{1}", i1,i2); Console.WriteLine("i1的值为:{0},i2的值为:{1},i6的值为{2}", i1, i2,i2); Console.WriteLine("i1的值为:{1},i2的值为:{1},i6的值为{2}", i1, i2, i2); } } }
数据类型
在 C# 中,变量分为以下几种类型:
-
值类型(基本数据类型)(Value types)
-
引用类型(Reference types)
-
指针类型(Pointer types)
值类型(Value types)
值类型:数值类型,布尔类型,字符类型,枚举类型,结构体
值类型变量可以直接分配给一个值。它们是从类 System.ValueType 中派生的。
值类型直接包含数据。比如 int、char、float,它们分别存储数字、字符、浮点数。当您声明一个 int 类型时,系统分配内存来存储值。
类型 | 描述 | 范围 | 默认值 |
---|---|---|---|
bool | 布尔值 | True 或 False | False |
byte | 8 位无符号整数 | 0 到 255 | 0 |
char | 16 位 Unicode 字符 | U +0000 到 U +ffff | '\0' |
decimal | 128 位精确的十进制值,28-29 有效位数 | (-7.9 x 10^28 到 7.9 x 10^28) / 10^(0 到 28) | 0.0M |
double | 64 位双精度浮点型 | (+/-)5.0 x 10^-324 到 (+/-)1.7 x 10^308 | 0.0D |
float | 32 位单精度浮点型 | -3.4 x 10^38 到 + 3.4 x 10^38 | 0.0F |
int | 32 位有符号整数类型 | -2,147,483,648 到 2,147,483,647 | 0 |
long | 64 位有符号整数类型 | -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 | 0 |
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 |
分类
-
数值类型
-
整数数值类型
-
有符号数:sbyte,int,short,long
-
无符号数:byte,ushort,ulong,unit
-
-
小数数值(浮点数)类型
-
单精度 float,赋值时,要加f
-
双精度double
-
-
-
布尔类型
-
关键字:bool
-
内容:true/false
-
-
字符类型
-
关键字:char,char c = 'a';
-
-
枚举类型
-
结构体
代码演示
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _04_数据类型_值类型 { internal class Program { static void Main(string[] args) { //值类型 / 基本数据类型 // 值类型的变量保存的是这个值本身 //数据类型-->值类型-->数值类型-->整数数值类型 //有符号 //无符号 //8 位无符号整数 范围 0-255 (字节类型,占用1个字节) byte b = 255; //8 位有符号整数类型 范围 -128 到 127 sbyte b1 = -127; //16 位有符号整数类型 范围 -32,768 到 32,767 (占用两个字节) short s = 32767; //16 位无符号整数类型 范围 0 到 65,535 ushort s1 = 65535; //32 位有符号整数类型 范围 -2,147,483,648 到 2,147,483,647 (占用四个字节) int i = 2147483647; //32 位无符号整数类型 范围 0 到 4,294,967,295 uint ui = 4294967295; //64 位无符号整数类型 范围 0 到 18,446,744,073,709,551,615 ulong u = 18446744073709551615; // 64 位有符号整数类型 范围-9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 long u1 = 9223372036854775807; //数据类型-->值类型-->数值类型-->小数数值(浮点数)类型 //单精度 //32 位单精度浮点型 大约是6-8位数字(占用4个字节) //注意:数字后面必须添加F或者f float f1 = 1.234567f; f1 = 222.1234567f; f1 = 123456.123456f; Console.WriteLine(f1); //双精度 // 64 位双精度浮点型 大约15-17位(占用8个字节) //数字后面不需要添加后缀,默认就是double类型 也可以添加后缀 d或者D double d1 = 1.123456789123456789; Console.WriteLine(d1); //128 位精确的十进制值,28-29 有效位数 decimal dec1 = 10.1203020302m; //总结: //整数:byte,sbyte<ushort,short<uint,int<ulong,long //小数:float<double<decimal //整数中能存负数的:sbyte short int long //数据类型-->值类型-->布尔类型 //用来表示真和假 只有两个值 true(真) false(假) bool bool1= true; bool1=false; //数据类型-- > 值类型-- > 字符类型 //用''包裹 只能存储一个字符 (数字,字母,中文...) char c1 = 'a'; c1 = '1'; c1 = '中'; //c1 = "中"; //c1 = '中文'; //数据类型-- > 值类型-- > 枚举类型 //限制一个变量只能是某几个值中的一个 //性别 //周几 //季节.... //使用: //1.定义枚举(只能定义在类中,不能定义在方法中) //2.定义变量的时候,设置该变量为这个枚举的类型 // 数据类型 变量=值; Sex sex= Sex.man; sex=Sex.women; //sex =Sex.menmen; Console.WriteLine(sex); } //定义在类里面: //格式: // enum 枚举的名字{ // 枚举成员 //} //枚举的名字以大写字母开头 //枚举类型的成员默认从0开始 enum Sex { man,//0 women//1 } } }
引用类型(Reference types)
引用类型不包含存储在变量中的实际数据,但它们包含对变量的引用。 换句话说,它们指的是一个内存位置。使用多个变量时,引用类型可以指向一个内存位置。如果内存位置的数据是由一个变量改变的,其他变量会自动反映这种值的变化。内置的 引用类型有:object、dynamic 和 string。
对象(Object)类型
object是所有类型的父类型,也就是说,object可以存储任意的数据类型,对象是C#中所有数据类型的终极基类(父类),所以对象类型可以存储任意类型的数据, 可以理解为:object表示为任意类型
字符串(String)类型
字符串(String)类型 允许您给变量分配任何字符串值。字符串(String)类型是 System.String 类的别名。它是从对象(Object)类型派生的。字符串(String)类型的值可以通过两种形式进行分配:引号和 @引号。
动态(dynamic)类型
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _02_数据类型_引用类型 { internal class Program { static void Main(string[] args) { // string 字符串类型 用于存储多个字符 //需要使用"" @"" $""包裹 // \n 换行 \t空格 string name1 = "吴\n亦\t凡"; Console.WriteLine(name1); //字符串中有一些字符不能直接使用 比如\n会被解析成换行 \t会被解析成空格 //需要使用\进行转义 string str = "\\n换行 \\t空格\""; Console.WriteLine(str); //@"" 创建的字符串可以不需要使用转义字符就可以显示这些特殊的字符(写啥就是啥) // " 比较特殊 需要使用""进行表示 string name2 = @"吴\n亦\t凡"""""; Console.WriteLine(name2); //$"" 可以直接使用{}插入变量 string name3 = "吴亦凡"; string address = "加拿大"; Console.WriteLine("吴亦凡在"+address); Console.WriteLine($"吴亦凡在{address}"); Console.WriteLine(name3+"是"+address+ "劣迹男艺人"); Console.WriteLine($"{name3}是{address}劣迹男艺人"); //--------------- //object类型 对象类型 //1.object是所有类型的父类型,也就是说,object可以存储任意的数据类型 object o1 = "123"; o1 = true; o1 = 1; //2.我们也可以定义自己的对象(自己的类),存储一系列相关的数据 // 记录每一个学生的信息 姓名 年龄 性别 学号 爱好 班级 ...... //42 //一个学生需要六个变量 需要200+个变量 //string name1 = "李增龙"; //int age1 = 19; //char sex = '男'; //...... //对象和类的关系? //类:是对一系列对象的描述,是一个模版,一个蓝图,学生类,动物类,汽车类.... //对象:是一个类的具体表现,我们也把对象称之为实例 .例如:学生类的实例 ,某个同学 //创建Student类的的对象 他就是Student的实例 //int a = 10; Student student1 = new Student(); student1.Name ="吴亦凡"; student1.Age = 20; student1.Sex = '男'; Student student2 = new Student(); student2.Name = "罗志祥"; student2.Age = 20; student2.Sex = '男'; // 动态(dynamic)类型 也可以存储任意类型的数据 dynamic d1 = 123; d1 = "123"; d1 = true; //dynamic类型的校验在运行期间进行(类型错误也可以运行保存) // Console.WriteLine(d1*100); //object虽然也可以存储任意类型,但是他的类型校验在编译过程中(类型错误的话,运行不了) object o2 = true; //Console.WriteLine(o2* 100); } } //驼峰命名法 user name //大驼峰 UserName 从第一个单词开始 首字母大写 //小驼峰 userName 从第二个单词开始 首字母大写 //下划线命名法 //user_name //在命名空间(namespace)中使用class定义一个类,类的名字使用大驼峰命名法 //类:不是对象 是对象的模版 就像我们平常所说的"人类" 不是一个人 而是"人"的模版 class Student { //类的内部的东西 我们称之为类的成员(属性) //声明一个类的成员的格式: public 成员的类型 成员的名称(一半也使用大驼峰命名法) public string Name; public int Age; public char Sex; } }
数据类型的区别
栈内存和堆内存
概念
堆(Heap):在c里面叫堆,在c#里面其实叫托管堆。
栈(Stack):就是堆栈,简称栈。
托管堆:
托管堆不同于堆,它是由CLR(公共语言运行库(Common Language Runtime))管理,当堆中满了之后,会自动清理堆中的垃圾。所以,做为.net开发,我们不需要关心内存释放的问题。
栈
栈区的特性:先进后出 入栈的时候是123按顺序入的,那么出栈的时候就是321按顺序出。
当一个栈结束时会把存在栈中的数据依次出栈。
内存堆栈:存在内存中的两个存储区(堆区,栈区)。
栈区:存放函数的参数、局部变量、返回数据等值,由编译器自动释放
堆区:存放着引用类型的对象,由CLR释放
堆
堆是一块内存区域,在堆里可以分配大块的内存用于存储某类型的数据对象。与栈不同,堆里的内存能够以任意顺序存入和移除,除了string 先进先出。虽然程序可以在堆里保存数据,但并不能显式地删除它们。CLR的自动GC(Garbage Collector,垃圾收集器)在判断出程序的代码将不会再访问某数据项时,自动清除无主的堆对象。
值类型和引用类型的区别
1.值类型存取速度快,引用类型存取速度慢
2.值类型表示实际数据,引用类型表示存储在堆中的数据的引用和地址。
3.值类型都继承自System.VlueTyp,引用类型都继承自System.Object。
4.栈中的内存是自由分配自动释放的,而引用类型会由.NET的GC(垃圾回收机制)来回收释放。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _03_数据类型的区别 { internal class Program { static void Main(string[] args) { //值类型的变量存储的是值本身(存储到栈内存中) //引用类型的变量存储的是数据的内存地址(存储到堆中,变量保存的是内存地址) //= 运算符在进行复制操作的时候,赋值的是被复制的变量栈内存中存储的内容 //= 运算符在进行赋值操作的时候,操作也是栈内存中存储的内容 int a = 1; int b = a; a = 2; Console.WriteLine(a);//2 Console.WriteLine(b);//1 Book book1 = new Book(); book1.Name = "西游记"; book1.Price = 100; Book book2 = book1; book1.Name = "红楼梦"; Console.WriteLine(book1.Name);//红楼梦 Console.WriteLine(book2.Name);//红楼梦 People p1=new People() { Name="吴亦凡"}; // p1.Name = "吴亦凡"; People p2 = p1; p1=new People() { Name="李易峰"}; Console.WriteLine(p1.Name);//李易峰 Console.WriteLine(p2.Name);//吴亦凡 Peoples laowang = new Peoples() { Name = "老王" }; Peoples xiaowang = new Peoples() { Name = "小王" }; xiaowang.Friend = laowang; Console.WriteLine($"名字叫做{xiaowang.Name},父亲叫做{xiaowang.Friend.Name}"); laowang.Name = "王强"; Console.WriteLine($"名字叫做{xiaowang.Name},父亲叫做{xiaowang.Friend.Name}"); Peoples aq = xiaowang; aq.Name = "阿强"; Console.WriteLine($"名字叫做{xiaowang.Name},父亲叫做{xiaowang.Friend.Name}"); // string 引用数据类型 //不是特别严谨的注释: 虽然字符串是引用数据类型,但是C#对它进行了特殊处理,可以把它当做基本数据类型使用 //后续详解 string s1 = "吴亦凡"; string s2 = s1; s1 = "李云迪"; Console.WriteLine(s1);//李云迪 Console.WriteLine(s2);//吴亦凡 } class Book { public string Name; public float Price; } class People { public string Name; } class Peoples { public string Name; //对象的成员也可以是一个对象 public Peoples Friend; } } }
特别注意:
在 C# 中,string 类型被视为引用类型。但是,与其他引用类型不同的是,对 string 变量的操作被视为对值类型的操作,因此,在对 string 变量进行赋值或传递到方法时,其实是进行值传递。
这意味着,当您将一个字符串变量传递给一个方法时,该方法会接收该字符串的一个副本,而不是原始字符串的引用。任何对传递的字符串副本的修改都不会影响原始字符串。
值类型和引用类型的区别在于,值类型存储在堆栈中,而引用类型存储在堆中。当您创建一个字符串变量时,它实际上是一个引用类型,但是 .NET 框架会将其优化为值类型,以提高性能。
需要注意的是,字符串是不可变的,即一旦创建就无法更改。因此,在对字符串进行操作时,实际上是创建了一个新的字符串对象,而不是修改原始字符串对象。这也是为什么 C# 中的字符串变量是值传递的原因。
总之,在 C# 中,string 类型是引用类型,但是在对其进行操作时,会被视为值类型,因此是值传递的。
值类型补充-结构体
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _04_值类型补充_结构体 { internal class Program { static void Main(string[] args) { //结构体可以让变量存储多个数据(值类型) //用法和形式与类基本相同,可以理解为:结构体是值类型的对象 //结构体的使用分为两步 //1.结构体的声明 //2.结构体创建 Book book1 = new Book() { Name = "西游记", Price = 99.9f, Count = 40, Describe = "猴子打妖怪" }; Book book2 = new Book() { Name = "红楼梦", Price =100.1f, Count = 80, Describe = "谈恋爱的故事" }; Book book3 = book1; book1.Name = "水浒传"; book1.Describe = "108男人的故事"; Console.WriteLine($"book1的名字是{book1.Name},售价{book1.Price},讲述了一个{book1.Describe}的故事"); Console.WriteLine($"book3的名字是{book3.Name},售价{book3.Price},讲述了一个{book3.Describe}的故事"); } } //1.结构体的声明(类似于类的声明) struct Book { public string Name; public float Price; public int Count; public string Describe; } }
可空数据类型
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _05_可空数据类型 { internal class Program { static void Main(string[] args) { //值类型能存储的数据只有这个类型的所有数据 //比如:int 只能存整数 bool 只能存true和false int a = 1; bool b = false; //引用类型 除了可以存储对应的类型外,还可以存储一个null值 string s = "123"; s=null; People p1=new People(); p1 = null; //可空数据类型就是让值类型可以存储null值 // //值类型都有默认值 //1.值类型的默认值都是0 //2.bool类型的默认值是false //3.char类型的默认值是'\0' //4.结构体的默认值是空的结构体 //在数据类型的后面 添加一个? 表示它是一个可空数据类型,将会在本来存储的范围上添加一个null值,同时他的默认值就为null int ? i2 = null; char? c2 = 'a'; //可空数据类型到不可控数据类型的转换 //如果一个值为可空数据类型,我们在运算的时候,就会出现问题,所有需要将可空数据类型转换为不空数据类型 //int num = 10 + i2; int? i3 = 10; //这样写报错,因为i3可以为null i4不能为null // int i4 = i3; //可以使用 ?? 运算符进行处理 // 执行逻辑: 如果??前面的值为null,则使用后面的值,如果前面的值不为null则使用前面的值 int i4 = i3 ?? 0; Console.WriteLine(i4); } } class People { public string Name; } }
控制台输入的接收
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _06_控制台输入的接收 { internal class Program { static void Main(string[] args) { //获取用户输入的字符,只能获取一个字符 ConsoleKeyInfo s = Console.ReadKey(); //通过ConsoleKeyInfo的Key.获取当前按下的键位 Console.WriteLine($"按下的键位是{s.Key}"); //通过ConsoleKeyInfo的KeyChar.获取当前按下的键值 Console.WriteLine($"按下的键值是{s.KeyChar}"); string str = Console.ReadLine(); Console.WriteLine(str); } } }