C#简介
C#(读作“See Sharp”)微软公司开发的一种面向对象且运行在.NetFramwork之上的高级程序设计语言。 开发人员利用 C# 能够生成在 .NET 生态系统中运行的多种安全可靠的应用程序。 C# 源于 C 语言系列,C、C++、Java 和 JavaScript 程序员很快就可以上手使用。
重要特性:
- 布尔条件(Boolean Conditions)
- 自动垃圾回收(Automatic Garbage Collection)
- 标准库(Standard Library)
- 组件版本(Assembly Versioning)
- 属性(Properties)和事件(Events)
- 委托(Delegates)和事件管理(Events Management)
- 易于使用的泛型(Generics)
- 索引器(Indexers)
- 条件编译(Conditional Compilation)
- 简单的多线程(Multithreading)
- LINQ 和 Lambda 表达式
- 集成 Windows
第一个程序
在VS中新建一个控制台,输入两个数字,输出数字相乘的结果。
1、创建新项目;
2、选择控制台;
3、配置项目信息;
4、开始编码;
语法
using 关键字:
using 关键字用于在程序中包含命名空间。一个程序可以包含多个 using 语句。
命名空间(Namespace):
设计目的是提供一种让一组名称与其他名称分隔开的方式。在一个命名空间中声明的类的名称与另一个命名空间中声明的相同的类的名称不冲突。
class 关键字:
class 关键字用于声明一个类。
成员变量:
变量是类的属性或数据成员,用于存储数据。
成员函数:
函数是一系列执行指定任务的语句。类的成员函数是在类内声明的。
变量
变量是一个名称,表示程序执行时存储在内存中的数据。每个变量都有特定的类型,类型决定了变量的内存大小和分布,可以对变量进行一系列操作。
变量声明:
变量在使用之前必须声明。变量声明定义了变量,并完成两件事:
1、给变量命名,并为它关联一种类型;
2、让编译器为它分配一块内存。
一个简单的变量声明至少需要一个类型和一个名称。
变量的作用域:
变量的作用域是可以访问该变量的代码区域,一般情况下,确定作用域遵循以下规则:
1.局部变量存在于表示声明该变量的块语句或方法结束的右花括号之前的作用域内。
2.在for、while或类似语句中声明的局部变量存在地该循环体内。
变量初始化
变量通过在等号后跟一个常量表达式进行初始化(赋值)。初始化的一般语法为:
控制台输入输出
Console.ReadLine()方法: 从控制台窗口读取一行文本,返回string值
Console.ReadKey()方法: 监听键盘事件,可以理解为按任意键执行
Console.Write()方法: 将制定的值写入控制台窗口
Console.WriteLine()方法: 将制定的值写入控制台窗口,但在输出结果的最后添加一个换行符
数据类型
基本数据类型(不止这些)
浮点类型(即有小数点的)
布尔类型
C#的基本类型布尔(boolean)类型bool,也称条件类型,bool的BLC名称为System.Boolen。
包含两个值:true和false。
只需要1bit的存储空间,但运行时会使用1byte内存(运行时和处理器可以高效操作的最小块)
字符串
创建 String 对象
可以使用以下方法之一来创建 string 对象:
1.通过给 String 变量指定一个字符串
2.通过使用 String 类构造函数
3.通过使用字符串串联运算符( + )
4.通过检索属性或调用一个返回字符串的方法
5.通过格式化方法来转换一个值或对象为它的字符串表示形式
数组(Array)
数组是一个存储相同类型元素的固定大小的顺序集合。数组是用来存储数据的集合,通常认为数组是一个同一类型变量的集合。
数组中某个指定的元素是通过索引来访问的,所有的数组都是由连续的内存位置组成的。最低的地址对应第一个元素,最高的地址对应最后一个元素。
声明数组
datatype[] arrayName;
datatype 用于指定被存储在数组中的元素的类型。
[ ] 指定数组的秩(维度)。秩指定数组的大小。
arrayName 指定数组变量的名称。
初始化数组
声明一个数组不会在内存中初始化数组。当初始化数组变量时,可以赋值给数组。
数组是一个引用类型,所以需要使用 new 关键字来创建数组的实例。
double[] balance = new double[10];
赋值给数组
double[] balance = new double[10];
示例1:balance[0] = 4500.0;
示例2:double[] balance = { 2340.0, 4523.69, 3421.0};
示例3:int [] marks = new int[5] { 99, 98, 92, 97, 95};
示例4:int [] marks = new int[] { 99, 98, 92, 97, 95};
访问数组元素
元素是通过带索引的数组名称来访问的。这是通过把元素的索引放置在数组名称后的方括号中来实现的。
double salary = balance[9];
枚举
枚举是一组命名整型常量,使用 enum 关键字声明的。C# 枚举是值类型,不能继承或传递继承。
声明 enum 变量
声明枚举的一般语法:
enum <enum_name>
{
enumeration list
};
- enum_name 指定枚举的类型名称。
- enumeration list 是一个用逗号分隔的标识符列表。
枚举列表中的每个符号代表一个整数值,一个比它前面的符号大的整数值。默认情况下,第一个枚举符号的值是 0.例如:
类型分类
所有C#类型分为以下类别:
值类型
引用类型
通用类型参数
指针类型
值类型包括大多数内置类型,例如,int,long,bool以及自定义结构和枚举类型。
引用类型包括所有类,数组,委托和接口类型。它还包括预定义的 string 类型。
值类型
值类型(Value types)
值类型变量可以直接分配给一个值,它们是从类 System.ValueType 中派生的。
值类型(value type):byte,short,int,long,float,double,decimal,char,bool 和 struct 统称为值类型。值类型变量声明后,不管是否已经赋值,编译器为其分配内存。
常用值类型:
引用类型
引用类型(Reference types)
引用类型不包含存储在变量中的实际数据,但它们包含对变量的引用,它们指的是一个内存位置。使用多个变量时,引用类型可以指向一个内存位置。如果内存位置的数据是由一个变量改变的,其他变量会自动反映这种值的变化。内置的 引用类型有:object、dynamic 和 string。
类型转换
类型转换方法
C# 提供了下列内置的类型转换方法,如:ToString()、ToInt32()、ToDateTime()、ToDecimal()、数据类型.Parse()、数据类型.TryParse()、Convert.ToInt32()等
数据类型在一定的条件下是可以相互转换的,如将int型数据转换成double型数据。C#允许使用两种转换方式:隐式转换和显式转换。
隐式转换
任何类型A,只要其取值范围完全包含在类型B的取值范围内,就可以隐式转换为类型B,C#的隐式转换不会导致数据丢失。
显式转换
从类型A到类型B的转换只能在某些情况下进行,转换规则比较复杂,应进行某种类型的额外处理。显式转换又叫强制类型转换,显式转换需要用户明确的指定转换类型。
注意:
(1)、显式转换可能会导致错误,进行这种转换时编译器将对转换进行溢出检测。如果有溢出说明转换失败,就表明源类型不是一个合法的目标类型。无法进行类型转换。
(2)、强制类型转换会造成数据丢失,如上面的例子中,最终得到的d值为10。
装箱&拆箱
装箱和拆箱
在值类型和对象之间进行转换时,CLR必须执行装箱和取消装箱的过程
装箱(隐式)在将值类型转换为引用类型时发生。
int i = 1;
object obj = i; // Box the int
拆箱(显式)在将引用类型转换为值类型时发生。
int i = 123; // a value type
object o = i; // boxing
int j = (int)o; // unboxing
拆箱需要显式强制转换,如果类型不匹配会引发异常,比如string转换为int。
装箱、拆箱会有一定的性能代价,在循环中要避免这类操作。
null、void、可空修饰符
null表明变量不引用任何有效的对象。void表示没有类型或没有任何值。
null只能赋能引用类型、指针类型、可空类型。将变量设置为空,会显示的设置引用,使它不指向任何位置。
void本质上不是一个数据类型,它用于指出没有数据类型这一事实。比如函数没有返回值,就需要在数据类型位置指定为void。
null不能赋给值类型,值类型不能包含引用,如果要将null赋能值类型,可以使用可空修饰符。如下:
int? count=null;
运算符
算术运算符
下表显示了 C# 支持的所有算术运算符。假设变量 A 的值为 10,变量 B 的值为 20,则:
关系运算符
下表显示了 C# 支持的所有关系运算符。假设变量 A 的值为 10,变量 B 的值为 20,则:
逻辑运算符
下表显示了 C# 支持的所有逻辑运算符。假设变量 A 为布尔值 true,变量 B 为布尔值 false,则:
位运算符
下表列出了 C# 支持的位运算符。~表示位逻辑非运算、&表示位逻辑与运算、|表示位逻辑或运算、^表示位逻辑异或运算、,<<表示位左移运算、>>表示位右移运算。假设变量 A 的值为 60,变量 B 的值为 13,则:
赋值运算符
赋值运算符的意思是将运算符右侧的对象或者数值传递给左侧的对象或者变量。
下表列出了 C# 支持的赋值运算符
控制流
判断
判断结构要求指定一个或多个要评估或测试的条件,以及条件为真时要执行的语句(必需的)和条件为假时要执行的语句(可选的)。
循环
循环类型
C# 提供了以下几种循环类型。
循环控制语句
循环控制语句更改执行的正常序列。当执行离开一个范围时,所有在该范围中创建的自动对象都会被销毁。
方法和参数
方法
什么是方法
方法(method)是一种用于实现可以由对象或类执行的计算或操作的成员,是一个已命名的语句集。方法就是把一些相关的语句组织到一起,用来执行一个任务的语句块。
格式:
修饰符 返回值类型 方法名称 (参数)
{
方法体
}
每个方法都有一个名称和一个主体。
方法名称:是一个有意义的标识符,应当描述出方法的用途。
方法主体:包含了调用方法时实际执行的语句。
修饰符:public ,private, static等等。
返回值类型:void, int ,double等等。
参数:方法可有有任务个数的参数,参数都有特定的数据类型。
值参数
概念:方法中的值参数传递的类型可以包括“值类型”和“引用类型”。
结论:被调用方法的参数在栈上分配内存, 值类型参数复制值,而引用类型复制对象的引用地址
引用参数
概念:
1、使用引用参数时,必须在方法的声明和调用中使用ref修饰符。
2、声明的传递参数必须是变量,且在调用前初始化值(值类型) 。
结论:被调用方法的参数不在栈上分配内存。实际上该参数使用的是调用方法参数的地址。所以在方法执行过程中如果内容发生变化,在方法调用结束后依然有效。
输出参数
概念:用于从方法体内把数据传出到调用代码,它们的行为与引用参数非常类似。
如同引用参数,输出参数有以下要求:
1、必须在声明和调用中都使用修饰符。输出参数的修饰符是out而不是ref。
2、和引用参数相似,实参必须是变量,而不能是其他类型的表达式。
例如,下面的代码声明了名称为MyMethod的方法,它带有单个输出参数。
void MyMethod ( out int val )//方法声明
{
val =100;
...
}
int y = 1;//实参变量
MyMethod ( out y );//方法调用
out与ref区别:
out 关键字会导致参数通过引用来传递。这与 ref 关键字类似,不同之处在于 ref 要求变量必须在传递之前进行初始化。若要使用 out 参数,方法定义和调用方法都必须显式使用 out 关键字。
面向对象
类
类的定义
类的定义是以关键字 class 开始,后跟类的名称。类的主体,包含在一对花括号内。下面是类定义的一般形式:
结构体
在 C# 中,结构体是值类型数据结构。它使得一个单一变量可以存储各种数据类型的相关数据。struct 关键字用于创建结构体。结构体是用来代表一个记录。
定义结构体
定义一个结构体必须使用 struct 语句,struct 语句为程序定义了一个带有多个成员的新的数据类型。
类 vs 结构
类和结构有以下几个基本的不同点:
- 类是引用类型,结构是值类型。
- 结构不能继承其他的结构或类。
- 结构不能声明默认的构造函数(无参构造函数)。
- 结构可以不使用 New 操作符即可被实例化
接口
接口定义了所有类继承接口时应遵循的语法合同。接口定义了语法合同 "是什么" 部分,派生类定义了语法合同 "怎么做" 部分。
接口定义了属性、方法和事件,这些都是接口的成员。接口只包含了成员的声明。接口提供了派生类应遵循的标准结构。
定义接口
接口使用 interface 关键字声明,它与类的声明类似。接口声明默认是 public 的。
继承
继承是面向对象程序设计中最重要的概念之一。继承允许我们根据一个类来定义另一个类,这使得创建和维护应用程序变得更容易。
当创建一个类时,不需要完全重新编写新的数据成员和成员函数,只需要设计一个新的类,继承了已有的类的成员即可。这个已有的类被称为的基类,这个新的类被称为派生类。继承的思想实现了属于关系。例如,哺乳动物属动物,狗属于哺乳动物,因此狗属于动物。
基类和派生类
一个类可以派生自多个类或接口,这意味着它可以从多个基类或接口继承数据和函数。
C# 中创建派生类的语法如下:
public class 动物
{
public string 颜色;
public void 吃东西()
{
...
}
}
public class 狗 : 动物
{
public void 叫()
{
...
}
}
多重继承指的是一个类别可以同时从多于一个父类继承行为与特征的功能。与单一继承相对,单一继承指一个类别只可以继承自一个父类。
C# 不支持多重继承。但可以使用接口来实现多重继承。
封装
封装被定义为"把一个或多个项目封闭在一个物理的或者逻辑的包中"。在面向对象程序设计方法论中,封装是为了防止对实现细节的访问。
抽象和封装是面向对象程序设计的相关特性。抽象允许相关信息可视化,封装则使开发者实现所需级别的抽象。
C# 封装根据具体的需要,设置使用者的访问权限,并通过 访问修饰符 来实现。
一个访问修饰符定义了一个类成员的范围和可见性。
C# 支持的访问修饰符如下所示:
public:所有对象都可以访问;
private:对象本身在对象内部可以访问;
protected:只有该类对象及其子类对象可以访问
internal:同一个程序集的对象可以访问;
protected internal:访问限于当前程序集或派生自包含类的类型。
比如说:一个人A为父类,他的儿子B,妻子C,私生子D(注:D不在他家里)
如果我们给A的事情增加修饰符:
public事件,地球人都知道,全公开
protected事件,A,B,D知道(A和他的所有儿子知道,妻子C不知道)
private事件,只有A知道
internal事件,A,B,C知道(A家里人都知道,私生子D不知道)
protected internal事件,A,B,C,D都知道,其它人不知道
多态
多态是同一个行为具有多个不同表现形式或形态的能力。多态性意味着有多重形式。在面向对象编程范式中,多态性往往表现为"一个接口,多个功能"。
多态就是同一个接口,使用不同的实例而执行不同操作,如图所示:
函数重载
可以在同一个范围内对相同的函数名有多个定义。函数的定义必须彼此不同,可以是参数列表中的参数类型不同,也可以是参数个数不同。不能重载只有返回类型不同的函数声明。
动态多态性
C# 允许使用关键字 abstract 创建抽象类,用于提供接口的部分类的实现。当一个派生类继承自该抽象类时,实现即完成。抽象类包含抽象方法,抽象方法可被派生类实现。
当有一个定义在类中的函数需要在继承类中实现时,可以使用虚方法。
虚方法是使用关键字 virtual 声明的。
虚方法可以在不同的继承类中有不同的实现。
对虚方法的调用是在运行时发生的。
动态多态性是通过 抽象类 和 虚方法 实现的
异常处理
异常是对程序运行时出现的特殊情况的一种响应。异常提供了一种把程序控制权从某个部分转移到另一个部分的方式。C# 异常处理时建立在四个关键词之上的:try、catch、finally 和 throw。
- try:一个 try 块标识了一个将被激活的特定的异常的代码块。后跟一个或多个 catch 块。
- catch:程序通过异常处理程序捕获异常。catch 关键字表示异常的捕获。
- finally:finally 块用于执行给定的语句,不管异常是否被抛出都会执行。例如,如果您打开一个文件,不管是否出现异常文件都要被关闭。
- throw:当问题出现时,程序抛出一个异常。使用 throw 关键字来完成。
语法
可以列出多个 catch 语句捕获不同类型的异常,以防 try 块在不同的情况下生成多个异常。
常用异常类
C# 异常是使用类来表示的。C# 中的异常类主要是直接或间接地派生于 System.Exception 类。
System.ApplicationException 和 System.SystemException 类是派生于 System.Exception 类的异常类。
System.ApplicationException 类支持由应用程序生成的异常。
System.SystemException 类是所有预定义的系统异常的基类。
下表列出了一些派生自 Sytem.SystemException 类的预定义的异常类:
自定义异常
为什么要自定义异常类
(1)没有具体系统异常相对应
(2)不希望在Catch块中处理
(3)希望能明确标志错误种类的异常
自定义异常类定义步骤
继承自System.ApplicationException类,并使用Exception作为后缀名。
示例如下:
推荐阅读
微软官方教程:
https://docs.microsoft.com/zh-cn/dotnet/csharp/
https://docs.microsoft.com/zh-cn/dotnet/
w3cschool、腾讯课堂、菜鸟教程等