C#内容与知识点整理(1)

首先对本系列进行简要说明:

本系列主要是针对C#编程语言的一些基本知识进行概述,旨在帮助下述类型的伙伴们:

(1)以C#程序设计作为期末考试科目并且所涉及到的内容不会过于深奥的;

(2)想要以游戏设计作为兴趣爱好或是准备从事游戏行业,需要简单上手C#语言的;

(3)需要拥有一定的C/C++基础(对于与C/C++高度重合的内容这个系列就不做展开了,有需要的朋友可以去CSDN等网站搜索相关教程)的;

(4)想对于C#的基础知识进行简要回顾复习的;

再次声明,本系列是对于C#的一些基本内容进行整理,以便于大家进行简要的复习,也欢迎大家在评论区里友好地评论,如果对于自己的编程能力非常满意的朋友或者是不想再基础知识点上浪费时间的朋友可以去找更深奥的文章和博客来阅读,不必拘泥于本系列,浪费您的宝贵时间.

另外,本系列内容参考的教材为《C#程序设计教程(第3版)》(李春葆,曾平,喻丹丹编著),感兴趣的朋友也可以购买阅读这本教材.

第一部分 C#语言基本知识

一.基础的语法和概念

1.C#标识符命名与C语言一致(同时,标识符不能与C#中的类库名相同)

2.C#中的数据类型

3.C#运算符和表达式(与C语言保持一致)

额外内容:字符串运算符(只有一个,就是+,表示将字符串连接起来,合并成新的字符串)

比如直观理解,如果a,b都为字符串类型,a+b就是将a,b两个字符串相连

其他运算符:

(1)sizeof运算符

sizeof运算符求值类型数据在内存中占用的字节数sizeof运算符的语法格式如下 sizeof(类型标识符)   其结果为一个整数,表示指定类型的数据在内存分配的字节数。该运算符只能作用于值类型或值类型变量。 :sizeof(string),string类型无法直接用sizeof求出.

重点:sizeof只能作用于值类型或者值类型变量

(2)typeof运算符(关于这一点在反射部分也会有进一步的介绍)

该运算符用于获得指定数据类型的说明。例如,若声明了结构Student,则以下语句输出声明Student结构类型的程序名等   Console.WriteLine(typeof(Student)); 输出:prog2_2.Program+Student 例如:Csharpfinalexamreview1.Program

(3)new运算符

该运算符用于创建一个类的对象

(4)checked和unchecked

​ checked 关键字用于对整型算术运算和转换显式启用溢出检查。

​ unchecked 关键字用于取消整型算术运算和转换的溢出检查。

使用checked语句的一段代码: checked { int n = 10; int i = int.MaxValue + n; Console.WriteLine(i); } 运行之后会显示下面一段信息: System.OverflowException:“算术运算导致溢出。” 上述代码如果没有使用checked语句,则会正常输出一个错误的值(是一个负数) 注意,默认情况下,在编译时和执行时不检查包含非常数项的表达式,这是因为溢出检查比较耗时.所以当无溢出危险时,不检查代码可以提高性能(个人认为,一般自己写的程序不存在溢出风险时可以不适用checked)

4.控制台应用程序的基本元素

(1)注释

(2)main方法

它为入口主函数,其特点如下: 1.Main方法是.exe程序的入口点,程序控制在该方法中开始和结束。 2.Main方法方法在类或结构的内部声明。它必须为静态方法,而不应为公共方法(在前面的例子中,它接受默认访问级别 private)。 3.Main方法具有void或int返回类型 所声明的 Main 方法可以具有包含命令行实参的 string[] 形参,也可以不具有这样的形参。形参读取为零索引的命令行参数。与C/C++不同,程序的名称视为第一个命令行参数。

(3)输入方法Console.ReadLine (4)输出方法Console.WriteLine

Console.WriteLine方法类似于C语言的printf函数,可以采用“{N[,M][:格式化字符串]}”的形式来格式化输出字符串,其中的参数含义如下。 花括号({}):用来在输出字符串中插入变量的值。 N:表示输出变量的序号,从0开始。 [,M][:格式化字符串]:可选项,其中M表示输出的变量所占的字符个数 补充:这个值为负数时,输出的变量按照左对齐方式排列;如果这个值为正数,输出的变量按照右对齐的方式排列 [:格式化字符串]:可选项,因为在向控制台输出时,常常需要指定输出字符串的格式。

(5)数据转换

由于ReadLine方法只能输入字符串,为了输入数值,需要进行数据类型的转换。C#中每个数据类型都是一个结构,它们都提供了Parse方法,以用于将数字的字符串表示形式转换为等效数值。例如: int d = int.Parse(12); (int.指的是类,.Parse指的是方法,后续会有更详细介绍) 再比如,single.Parse(x)就是将数字的字符串表示形式x转换为它的等效单精度浮点数字

重点:可以使用.Parse来进行类型的转换

二.关于显式,隐式转换和装箱拆箱

值类型之间的转换有隐式转换和显示转换,另外,还有一种类型转换是装箱和拆箱.

1.显示和隐式转换

(1)隐式转换

以下为系统默认可以不加声明进行的转换

(2)显示转换

显式转换又叫强制类型转换,与隐式转换相反,显式转换需要用户明确地指定转换类型,一般在不存在该类型的隐式转换时才使用。格式如下: (类型标识符)表达式 其作用是将“表达式”值的类型转换为“类型标识符”的类型。例如: (int)1.23 //把double类型的1.23转换成int类型,结果为1

需要注意以下两点:

(1)显式转换可能会导致错误。进行这种转换时编译器将对转换进行溢出检测。如果有溢出说明转换失败,就表明源类型不是一个合法的目标类型,转换就无法进行。 (2)对于从float、double、decimal到整型数据的转换,将通过舍入得到最接近的整型值,如果这个整型值超出目标类型的范围,则出现转换异常。

(3)装箱和拆箱(这一部分需要对栈和堆的知识有所了解,在本篇后半部分有所介绍)

(i)装箱转换

装箱转换是指将一个值类型的数据隐式地转换成一个对象类型的数据。例如,下面语句就执行了装箱转换:   int i=10;   object obj=i;

重点:装箱可以将一个值类型的数据隐式地转换成一个对象类型的数据(理解成包装在箱子里送进堆空间当中)

(ii)拆箱转换

拆箱转换是指将一个引用类型的数据显式地转换成一个值类型数据。 拆箱操作分为两步:首先检查对象实例,确保它是给定值类型的一个装箱值,然后把实例的值复制到值类型数据中。例如,下面两条语句就执行了拆箱转换:   object obj=10;   int i=(int)obj; //拆箱

重点:拆箱转换是指将一个引用类型的数据显式地转换成一个值类型数据。(理解成从堆空间中拿出来一个箱子拆开)

相对于C/C++语言来说,C#的值类型和引用类型还是有一些地方有区别的,所以接下来还是简要的介绍一下C#的值类型和引用类型:

三.关于值类型与引用类型

​ 值类型的变量内含变量值本身,C#的值类型可以分为简单类型、结构类型和枚举类型.

(1)首先是简单类型:

(i)整数类型

需要掌握以下类型:sbyte,byte,short,ushort,int,uint,long,ulong

(注意,sbyte指带符号的字符型,而byte表示不带符号的字符型,u开头的为u符号类型,不取负数,范围在正数范围比较大,如下图)

(ii)实数类型

包括float,double,decimal

值得注意,decimal指的是固定精度的浮点数,其取值范围为

在声明变量的时候需使用 decimal d=1.23M(注意这个M/或者m)

(iii)字符类型

注意:在表示一个字符常数时,单引号内的有效字符数量必须且只能是一个,而且不能是单引号或者反斜杠(\)

char c=\x0048;//字符H,16进制转义符(前缀为\x) char c=\u0048;//字符H,Unicode表示形式(前缀为\u)

(iv)布尔类型

布尔类型的类型标识符为bool

注意:在C#语言中,bool类型不能像C++语言那样可能直接转换为int类型,例如,int a=(2<3);在C/C++中都是正确的,但在C#中不允许这样,会出现“无法将类型bool隐式转换为int”的编译错误(如下图所示)。

(2)然后是结构类型:

(i)结构类型的声明: 结构类型由若干"成员"组成,数据成员称为字段,每个字段都有自己的数据类型,一般声明结构类型的格式为:

struct 结构类型名称  { [字段访问修饰符] 数据类型 字段1; [字段访问修饰符] 数据类型 字段2; ... [字段访问修饰符] 数据类型 字段n;  };

(ii)结构类型变量的定义:

声明一个结构类型后,可以定义该结构类型的变量(简称为结构变量)。定义结构变量的一般格式如下:

结构类型 结构变量;

比如说,想要声明并定义一个Student结构体,则代码如下:

struct Student //声明结构类型Student { public int xh; //学号 public string xm; //姓名 public string xb; //性别 public int nl; //年龄 public string bh; //班号 }; Student s1,s2;//结构类型变量的定义 注意,struct和enum都不能直接放在Main方法中,否则会报错

(iii)结构变量的使用:

(iiia)访问结构变量字段

结构变量名.字段名(如s1.xm)

结构体变量的字段可以在程序中单独使用,与普通变量完全相同。

(iiib)结构变量的赋值

结构变量的赋值有两种方式。 (1)结构变量的字段赋值:使用方法与普通变量相同。 (2)结构变量之间赋值:要求赋值的两个结构变量必须类型相同。例如:   s1=s2; 这样s2的所有字段值赋给s1的对应字段

下面代码说明了结构类型的应用(只写出了主函数)

static void Main(string[] args) { Student s1,s2; //定义两个结构类型变量 s1.xh = 101; s1.xm = "李明"; s1.xb = "男"; s1.nl = 20; s1.bh = "07001"; Console.WriteLine("学号:{0},姓名:{1},性别:{2},年龄:{3}, 班号:{4}", s1.xh, s1.xm, s1.xb, s1.nl, s1.bh); s2 = s1; //将结构变量s1赋给s2 s2.xh = 108; s2.xm = "王华"; Console.WriteLine("学号:{0},姓名:{1},性别:{2},年龄:{3}, 班号:{4}", s2.xh, s2.xm, s2.xb, s2.nl, s2.bh); Console.WriteLine("学号:{0},姓名:{1},性别:{2},年龄:{3}, 班号:{4}", s1.xh, s1.xm, s1.xb, s1.nl, s1.bh); } }

注意:在执行s2=s1并修改s2的值之后,s1并没有改变,说明s1,s2存储在不同的位置(值类型变量)

(3)然后是枚举类型

枚举类型也是一种自定义数据类型,它允许用符号代表数据。枚举是指程序中某个变量具有一组确定的值,通过“枚举”可以将其值一一列出来。 1. 枚举类型的声明 枚举类型使用enum关键字声明,其一般语法形式如下: enum 枚举名 {枚举成员1,枚举成员2,...} 其中,enum是结构类型的关键字 例如,以下声明一个名称为color的表示颜色的枚举类型 enum Color {Red,Green,Blue,White,Black} 在声明枚举类型后,可以通过枚举名来访问枚举成员,其使用语法如下: 枚举名.枚举成员 2.枚举成员的赋值(这一点跟C语言类似) 在声明的枚举类型中,每一个枚举成员都有一个相对应的常量值,默认情况下C#规定第1个枚举成员的值取0,它后面的每一个枚举成员的值按加1递增。例如,前面Color中Red值为0Green值为1Blue值为2,依次类推。 可以为一个或多个枚举成员赋整型值,当某个枚举成员赋值后,其后的枚举成员没有赋值的话,自动在前一个枚举成员值之上加1作为其值。例如: enum Color { Red=0, Green, Blue=3, White, Black=1};//注意enum Color后面和{之间是没有等号的 则这些枚举成员的值分别为01341 3.枚举类型变量的定义 声明一个枚举类型后,可以定义该枚举类型的变量(简称为枚举变量)。定义枚举变量的一般格式如下: 枚举类型 枚举变量; 例如,在前面的枚举类型Color声明后,定义它的两个变量如下: Color c1,c2; 4.枚举变量的使用; 1)枚举变量的赋值 枚举变量赋值的语法格式如下: 枚举变量=枚举名.枚举成员 例如: c1=Color.Red; 2)枚举变量的访问 枚举变量像普通变量一样直接访问。

接下来是引用类型:

引用类型也称为参考类型.和值类型相比,引用类型的变量不直接存储所包含的值,而是指向它所要存储的值。类似C中的指针(包括类,数组,委托,接口)。(关于C#的委托,接口,类等概念后续会详细介绍)

(i)object类

object是C#中所有类型(包括所有的值类型和引用类型)的基类,C#中的所有类型都直接或间接地从object类中继承而来。因此,对一个object的变量可以赋予任何类型的值。

float f=1.23; object obj1;//定义obj对象 obj1=f; object obj2="China";//定义obj2对象并给其赋初值

(ii)string类

C#还定义了一个string类,表示一个Unicode字符序列,专门用于对字符串的操作。

​ 同样,这个类也是在.NET Framework的命名空间System中定义的,是类System.String的别名。

一些基本的字符串处理方式:

string str1 = "123" + "abc";//+用于连接两个字符串 char c = "Helloworld!"[2];//"[]"运算符可以访问string中的单个字符,c=e string str2 = "CHINA!"; string str3 = @"CHINA//\\";// 字符串的另一种表示形式,用@引起来(这个时候,转义符不会被转义)。 bool b = (str2 == str3);//"=="运算符用于两个字符串比较,b=true

对于其他类型在后续还会进行展开介绍

[补充知识]关于值类型变量和引用类型变量的区别

程序的内存空间分为栈空间和堆空间,值类型的数据在栈空间中分配,而引用类型数据(对象)在堆空间中分配。

(1)栈空间:

栈空间是一种先进后出的数据结构,用来存储如下类型数据:

(i)某些类型变量的值

(ii)程序当前运行环境

(iii)传递给方法的参数

(2)堆空间

堆空间是一块内存空间,在堆空间中可以分配大块的内存以存储某类型的数据对象。

与栈不同,堆里的空间能够以任意顺序存入和移除。

下图为一个程序在一个堆里面存放了3个数据;

如何更好地理解值类型的变量?

C#中的值类型变量和C/C++语言中的普通变量(非指针变量)相似,这类变量直接包含它们的值。所有的值类型均隐式派生自 System.ValueType。 C#中在内存的栈空间中为值类型变量分配空间,而且没有单独的堆分配或垃圾回收开销。因此值类型变量会随着方法调用后栈空间的消亡而自动清除 (C#中的值类型变量像C/C++语言中函数内的局部变量,函数执行完后由系统释放其存储空间)。 如下图:int x;int y=2;x=y;(就像上面的结构体,x和y虽然都代表2,但是是不同的2)

(当把变量y的值赋值给x的时候,程序会创建变量y的值的副本,也就是2,并把这个值放到x中,如上如所示,如果后面的程序修改了y的值,则x的值不会受到影响)

如何更好地理解引用类型的变量?

首先,在C/C++中可以定义指针变量,如:

char *p;//p是指针变量,存放某个字符变量的地址

在C#中没有指针,而是改为引用,引用表示某个对象的地址而不是变量/对象本身

注意:引用类型变量也是在栈空间中分配相应的存储空间的,只是引用类型变量所指向的对象是在托管堆上分配内存空间的

为什么会这样呢?首先来看一段C++函数:

void fun() { char *p; //定义指针变量p int i; //定义整型变量i p=(char *)malloc(11*sizeof(char)); //为p分配11个字符的空间 for (i=0;i<10;i++) //为10个字符单元分别赋值a~j   *(p+i)=a+i; *(p+i)=’\0; printf("%s\n",p); //输出:abcdefghij }//注意,函数执行后p所指向的内存空间并没有释放

.NET Framework改进了这一点:

将C#应用程序的执行置于CLR的监控之下,而且所有引用类型变量所指向的对象(其生命周期是全局性的)都在托管堆上分配空间,程序执行完毕,由CLR将堆空间全部回收,这样就不会出现像前面C/C++程序出现的内存泄漏问题了。

  所以,在C#中,当定义一个引用类型变量时,系统在栈空间中为该引用变量分配存储空间,要想创建对象并把对象的存储地址赋给该变量,就需要使用new操作符。例如:

  MyClass var; //MyClass是已定义的类或类型,var是变量存储在栈当中

  var=new MyClass(); //创建var引用的实例,使得var指向堆中的对象的存储地址

下面以数组引用类型变量为例进一步说明引用类型变量和值变量的区别:

int[] a = new int[3] { 1, 2, 3}; //定义一个数组a for (int i = 0; i < 3; i++) //输出数组a的所有元素 Console.Write("{0} ",a[i]); Console.WriteLine(); int[] b = a; //定义数组b并赋值为a for (int i = 0; i < 3; i++) //修改数组b的元素 b[i] *= 2; for (int i = 0; i < 3; i++) //再次输出数组a的所有元素 Console.Write("{0} ", a[i]); Console.WriteLine();

最后,b的值改变最终会影响到a,也就是改变a数组原来的值(这一点与值类型不同)

C#中,引用类型变量指向的实例都是没有名称的,所以不再使用指针的概念,引用类型变量指向的实例统一在堆空间中分配存储空间,以便于统一收回,这也是C#语言更加安全的特性之一 注意: 字符串的行为方式有所不同.因为字符串是不可变的(如果改变字符串的值,就会创建一个全新的字符串),所以字符串无法采用一般引用类型的行为方式 在方法调用中,对字符串所做的任何改变都不会影响原始字符串

对于引用型变量的复制:

int []b=a;

补充:关于常量的知识:

(1)直接常量

直接常量是指把程序中不变的量直接硬编码为数值或字符串值例如以下都是直接常量 100 //整型直接常量 1.23e5 //浮点型直接常量

(2)符号常量

符号常量是通过关键字const声明的常量,包括常量的名称和它的值。常量声明的格式如下: const 数据类型 常量名=初始值; 其中,“常量名”必须是C#的合法标识符,在程序中通过常量名来访问该常量。“类型标识符”指示了所定义的常量的数据类型,而“初始值”是所定义的常量的值。 例如: const double PI=2.14159265; 以下代码是错误的,因为将一个常量的值用一个变量来初始化了: int i=10; const int a=i;//错误

下一篇将于明晚(2021-3-9)进行更新,如果读者发现有错误还烦请指正,不胜感激,谢谢大家!