首页 > 代码库 > c#基础
c#基础
1. C# 程序结构
一个 C# 程序主要包括以下部分:
- 命名空间声明(Namespace declaration)
- 一个 class
- Class 方法
- Class 属性
- 一个 Main 方法
- 语句(Statements)& 表达式(Expressions)
- 注释
2. C# 基本语法
C# 是一种面向对象的编程语言。
在面向对象的程序设计方法中,程序由各种相互交互的对象组成。
相同种类的对象通常具有相同的类型,或者说,是在相同的 class 中。
例如,以 Rectangle(矩形)对象为例。
它具有 length 和 width 属性。
根据设计,它可能需要接受这些属性值、计算面积和显示细节。
using 关键字
在任何 C# 程序中的第一条语句都是:
using System;
using 关键字用于在程序中包含命名空间。
一个程序可以包含多个 using 语句。
class 关键字
class 关键字用于声明一个类。
成员变量
变量是类的属性或数据成员,用于存储数据。
成员函数
函数是一系列执行指定任务的语句。
类的成员函数是在类内声明的。
标识符
标识符是用来识别类、变量、函数或任何其它用户定义的项目。
在 C# 中,类的命名必须遵循如下基本规则:
- 标识符必须以字母、下划线或 @ 开头,后面可以跟一系列的字母、数字( 0 - 9 )、下划线( _ )、@。
- 标识符中的第一个字符不能是数字。
- 标识符必须不包含任何嵌入的空格或符号,比如 ? - +! # % ^ & * ( ) [ ] { } . ; : " ‘ / \。
- 标识符不能是 C# 关键字。除非它们有一个 @ 前缀。 例如,@if 是有效的标识符,但 if 不是,因为 if 是关键字。
- 标识符必须区分大小写。大写字母和小写字母被认为是不同的字母。
- 不能与C#的类库名称相同。
C# 关键字
关键字是 C# 编译器预定义的保留字。
这些关键字不能用作标识符,但是,如果您想使用这些关键字作为标识符,可以在关键字前面加上 @ 字符作为前缀。
在 C# 中,有些标识符在代码的上下文中有特殊的意义,如 get 和 set,这些被称为上下文关键字(contextual keywords)。
3. C# 数据类型
在 C# 中,变量分为以下几种类型:
- 值类型(Value types)
- 引用类型(Reference types)
- 指针类型(Pointer types)
值类型(Value types)
值类型变量可以直接分配给一个值。
它们是从类 System.ValueType 中派生的。
值类型直接包含数据。比如 int、char、float,它们分别存储数字、字母、浮点数。
当您声明一个 int 类型时,系统分配内存来存储值。
如需得到一个类型或一个变量在特定平台上的准确尺寸,可以使用 sizeof 方法。
表达式 sizeof(type) 产生以字节为单位存储对象或类型的存储尺寸。
引用类型(Reference types)
引用类型不包含存储在变量中的实际数据,但它们包含对变量的引用。
换句话说,它们指的是一个内存位置。使用多个变量时,引用类型可以指向一个内存位置。如果内存位置的数据是由一个变量改变的,其他变量会自动反映这种值的变化。内置的 引用类型有:object、dynamic 和 string。
对象(Object)类型
对象(Object)类型 是 C# 通用类型系统(Common Type System - CTS)中所有数据类型的终极基类。Object 是 System.Object 类的别名。所以对象(Object)类型可以被分配任何其他类型(值类型、引用类型、预定义类型或用户自定义类型)的值。但是,在分配值之前,需要先进行类型转换。
当一个值类型转换为对象类型时,则被称为 装箱;另一方面,当一个对象类型转换为值类型时,则被称为 拆箱。
object obj;
obj = 100; // 这是装箱
动态(Dynamic)类型
您可以存储任何类型的值在动态数据类型变量中。这些变量的类型检查是在运行时发生的。
声明动态类型的语法:
dynamic <variable_name> = value;
例如:
dynamic d = 20;
动态类型与对象类型相似,但是对象类型变量的类型检查是在编译时发生的,而动态类型变量的类型检查是在运行时发生的。
字符串(String)类型
字符串(String)类型 允许您给变量分配任何字符串值。
字符串(String)类型是 System.String 类的别名。
它是从对象(Object)类型派生的。
字符串(String)类型的值可以通过两种形式进行分配:引号和 @引号。
例如:
String str = "runoob.com";
一个 @引号字符串:
@"runoob.com";
C# string 字符串的前面可以加 @(称作"逐字字符串")将转义字符(\)当作普通字符对待,比如:
string str = @"C:\Windows";
等价于:
string str = "C:\\Windows";
@ 字符串中可以任意换行,换行符及缩进空格都计算在字符串长度之内。
string str = @"<script type=""text/javascript"">
<!--
-->
</script>";
用户自定义引用类型有:class、interface 或 delegate。
我们将在以后的章节中讨论这些类型。
指针类型(Pointer types)
指针类型变量存储另一种类型的内存地址。
C# 中的指针与 C 或 C++ 中的指针有相同的功能。
声明指针类型的语法:
type* identifier;
例如:
char* cptr;
int* iptr;
我们将在章节"不安全的代码"中讨论指针类型
4. C# 类型转换
类型转换从根本上说是类型铸造,或者说是把数据从一种类型转换为另一种类型。在 C# 中,类型铸造有两种形式:
- 隐式类型转换 - 这些转换是 C# 默认的以安全方式进行的转换, 不会导致数据丢失。例如,从小的整数类型转换为大的整数类型,从派生类转换为基类。
- 显式类型转换 - 显式类型转换,即强制类型转换。显式转换需要强制转换运算符,而且强制转换会造成数据丢失。
C# 类型转换方法
5. C# 变量
一个变量只不过是一个供程序操作的存储区的名字。
在 C# 中,每个变量都有一个特定的类型,类型决定了变量的内存大小和布局。
范围内的值可以存储在内存中,可以对变量进行一系列操作。
我们已经讨论了各种数据类型。
C# 中提供的基本的值类型大致可以分为以下几类:
类型 |
举例 |
整数类型 |
sbyte、byte、short、ushort、int、uint、long、ulong 和 char |
浮点型 |
float 和 double |
十进制类型 |
decimal |
布尔类型 |
true 或 false 值,指定的值 |
空类型 |
可为空值的数据类型 |
C# 允许定义其他值类型的变量,比如 enum
也允许定义引用类型变量,比如 class。
接受来自用户的值
System 命名空间中的 Console 类提供了一个函数 ReadLine(),用于接收来自用户的输入,并把它存储到一个变量中。
例如:
int num;
num = Convert.ToInt32(Console.ReadLine());
函数 Convert.ToInt32() 把用户输入的数据转换为 int 数据类型,因为 Console.ReadLine() 只接受字符串格式的数据。
6. C# 运算符
运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。C# 有丰富的内置运算符,分类如下:
- 算术运算符
- 关系运算符
- 逻辑运算符
- 位运算符
- 赋值运算符
- 其他运算符
7. 常量
字符串常量
字符常量是括在双引号 "" 里,或者是括在 @"" 里。
字符串常量包含的字符与字符常量相似,可以是:普通字符、转义序列和通用字符
使用字符串常量时,可以把一个很长的行拆成多个行,可以使用空格分隔各个部分。
这里是一些字符串常量的实例。
定义常量
常量是使用 const 关键字来定义的 。
8. C# 判断
判断结构要求程序员指定一个或多个要评估或测试的条件,以及条件为真时要执行的语句(必需的)和条件为假时要执行的语句(可选的)。
? : 运算符
我们已经在前面的章节中讲解了 条件运算符 ? :,可以用来替代 if...else 语句。它的一般形式如下:
Exp1 ? Exp2 : Exp3;
9. C# 循环
有的时候,可能需要多次执行同一块代码。
一般情况下,语句是顺序执行的:函数中的第一个语句先执行,接着是第二个语句,依此类推。
循环控制语句
循环类型
无限循环
如果条件永远不为假,则循环将变成无限循环。for 循环在传统意义上可用于实现无限循环。由于构成循环的三个表达式中任何一个都不是必需的,您可以将某些条件表达式留空来构成一个无限循环。
当条件表达式不存在时,它被假设为真。
您也可以设置一个初始值和增量表达式,但是一般情况下,程序员偏向于使用 for(;;) 结构来表示一个无限循环。
10. C# 封装
封装 被定义为"把一个或多个项目封闭在一个物理的或者逻辑的包中"。
在面向对象程序设计方法论中,封装是为了防止对实现细节的访问。
抽象和封装是面向对象程序设计的相关特性。
抽象允许相关信息可视化,封装则使开发者实现所需级别的抽象。
C# 封装根据具体的需要,设置使用者的访问权限,并通过 访问修饰符 来实现。
一个 访问修饰符 定义了一个类成员的范围和可见性。
C# 支持的访问修饰符如下所示:
- Public:所有对象都可以访问;
- Private:对象本身在对象内部可以访问;
- Protected:只有该类对象及其子类对象可以访问
- Internal:同一个程序集的对象可以访问;
- Protected internal:一个程序集内的对象,或者该类对象及其子类可以访问;
11. C# 方法
一个方法是把一些相关的语句组织在一起,用来执行一个任务的语句块。
每一个 C# 程序至少有一个带有 Main 方法的类。
C# 中调用方法
可以使用方法名调用方法。
也可以使用类的实例从另一个类中调用其他类的公有方法。
递归方法调用
一个方法可以自我调用。这就是所谓的 递归。
参数传递
当调用带有参数的方法时,您需要向方法传递参数。
方式 |
描述 |
值参数 |
这种方式复制参数的实际值给函数的形式参数,实参和形参使用的是两个不同内存中的值。在这种情况下,当形参的值发生改变时,不会影响实参的值,从而保证了实参数据的安全。 |
引用参数 |
这种方式复制参数的内存位置的引用给形式参数。这意味着,当形参的值发生改变时,同时也改变实参的值。 |
输出参数 |
这种方式可以返回多个值。 |
按值传递参数
这是参数传递的默认方式。
在这种方式下,当调用一个方法时,会为每个值参数创建一个新的存储位置。
实际参数的值会复制给形参,实参和形参使用的是两个不同内存中的值。
所以,当形参的值发生改变时,不会影响实参的值,从而保证了实参数据的安全。
按引用传递参数
引用参数是一个对变量的内存位置的引用。
当按引用传递参数时,与值参数不同的是,它不会为这些参数创建一个新的存储位置。
引用参数表示与提供给方法的实际参数具有相同的内存位置。
在 C# 中,使用 ref 关键字声明引用参数。
按输出传递参数
return 语句可用于只从函数中返回一个值。
但是,可以使用 输出参数 来从函数中返回两个值。
输出参数会把方法输出的数据赋给自己,其他方面与引用参数相似。
提供给输出参数的变量不需要赋值。
当需要从一个参数没有指定初始值的方法中返回值时,输出参数特别有用。
12. C# 可空类型(Nullable)
C# 提供了一个特殊的数据类型,nullable 类型(可空类型),可空类型可以表示其基础值类型正常范围内的值,再加上一个 null 值。
在处理数据库和其他包含可能未赋值的元素的数据类型时,将 null 赋值给数值类型或布尔型的功能特别有用。
例如,数据库中的布尔型字段可以存储值 true 或 false,或者,该字段也可以未定义。
Null 合并运算符( ?? )
Null 合并运算符用于定义可空类型和引用类型的默认值。
Null 合并运算符为类型转换定义了一个预设值,以防可空类型的值为 Null。Null 合并运算符把操作数类型隐式转换为另一个可空(或不可空)的值类型的操作数的类型。
如果第一个操作数的值为 null,则运算符返回第二个操作数的值,否则返回第一个操作数的值。
double? num1 = null;
double? num2 = 3.14157;
double num3;
num3 = num1 ?? 5.34;
Console.WriteLine("num3 的值: {0}", num3);
13. C# 数组(Array)
数组是一个存储相同类型元素的固定大小的顺序集合。
数组是用来存储数据的集合,通常认为数组是一个同一类型变量的集合。
初始化数组
声明一个数组不会在内存中初始化数组。
当初始化数组变量时,您可以赋值给数组。
数组是一个引用类型,所以您需要使用 new 关键字来创建数组的实例。
例如:
double[] balance = new double[10];
赋值给数组
您可以通过使用索引号赋值给一个单独的数组元素
访问数组元素
元素是通过带索引的数组名称来访问的。
这是通过把元素的索引放置在数组名称后的方括号中来实现的。
使用 foreach 循环
在前面的实例中,我们使用一个 for 循环来访问每个数组元素。
您也可以使用一个 foreach 语句来遍历数组。
C# 数组细节
在 C# 中,数组是非常重要的,且需要了解更多的细节。下面列出了 C# 程序员必须清楚的一些与数组相关的重要概念:
概念 |
描述 |
多维数组 |
C# 支持多维数组。多维数组最简单的形式是二维数组。 |
交错数组 |
C# 支持交错数组,即数组的数组。 |
传递数组给函数 |
您可以通过指定不带索引的数组名称来给函数传递一个指向数组的指针。 |
参数数组 |
这通常用于传递未知数量的参数给函数。 |
Array 类 |
在 System 命名空间中定义,是所有数组的基类,并提供了各种用于数组的属性和方法。 |
14. C# 字符串(String)
在 C# 中,您可以使用字符数组来表示字符串
但是,更常见的做法是使用 string 关键字来声明一个字符串变量。
string 关键字是 System.String 类的别名。
创建 String 对象
您可以使用以下方法之一来创建 string 对象:
- 通过给 String 变量指定一个字符串
- 通过使用 String 类构造函数
- 通过使用字符串串联运算符( + )
- 通过检索属性或调用一个返回字符串的方法
- 通过格式化方法来转换一个值或对象为它的字符串表示形式
String 类的属性
String 类有以下两个属性:
序号 |
属性名称 & 描述 |
1 |
Chars |
2 |
Length |
String 类的方法
String 类有许多方法用于 string 对象的操作。
15. C# 结构(Struct)
在 C# 中,结构是值类型数据结构。
它使得一个单一变量可以存储各种数据类型的相关数据。
struct 关键字用于创建结构。
结构是用来代表一个记录。
假设您想跟踪图书馆中书的动态。
定义结构
为了定义一个结构,您必须使用 struct 语句。
struct 语句为程序定义了一个带有多个成员的新的数据类型。
C# 结构的特点
您已经用了一个简单的名为 Books 的结构。
在 C# 中的结构与传统的 C 或 C++ 中的结构不同。
C# 中的结构有以下特点:
- 结构可带有方法、字段、索引、属性、运算符方法和事件。
- 结构可定义构造函数,但不能定义析构函数。但是,您不能为结构定义默认的构造函数。默认的构造函数是自动定义的,且不能被改变。
- 与类不同,结构不能继承其他的结构或类。
- 结构不能作为其他结构或类的基础结构。
- 结构可实现一个或多个接口。
- 结构成员不能指定为 abstract、virtual 或 protected。
- 当您使用 New 操作符创建一个结构对象时,会调用适当的构造函数来创建结构。与类不同,结构可以不使用 New 操作符即可被实例化。
- 如果不使用 New 操作符,只有在所有的字段都被初始化之后,字段才被赋值,对象才被使用。
类 vs 结构
类和结构有以下几个基本的不同点:
- 类是引用类型,结构是值类型。
- 结构不支持继承。
- 结构不能声明默认的构造函数。
16. C# 枚举(Enum)
枚举是一组命名整型常量。
枚举类型是使用 enum 关键字声明的。
C# 枚举是值数据类型。
换句话说,枚举包含自己的值,且不能继承或传递继承。
枚举列表中的每个符号代表一个整数值,一个比它前面的符号大的整数值。
17. C# 类(Class)
当你定义一个类时,你定义了一个数据类型的蓝图。
这实际上并没有定义任何的数据,但它定义了类的名称意味着什么,也就是说,类的对象由什么组成及在这个对象上可执行什么操作。
对象是类的实例。
构成类的方法和变量称为类的成员。
类的定义
类的定义是以关键字 class 开始,后跟类的名称。
类的主体,包含在一对花括号内。
请注意:
- 访问标识符 <access specifier> 指定了对类及其成员的访问规则。如果没有指定,则使用默认的访问标识符。类的默认访问标识符是 internal,成员的默认访问标识符是 private。
- 数据类型 <data type> 指定了变量的类型,返回类型 <return type> 指定了返回的方法返回的数据类型。
- 如果要访问类的成员,你要使用点(.)运算符。
- 点运算符链接了对象的名称和成员的名称。
成员函数和封装
类的成员函数是一个在类定义中有它的定义或原型的函数,就像其他变量一样。
作为类的一个成员,它能在类的任何对象上操作,且能访问该对象的类的所有成员。
成员变量是对象的属性(从设计角度),且它们保持私有来实现封装。
这些变量只能使用公共成员函数来访问。
C# 中的构造函数
类的 构造函数 是类的一个特殊的成员函数,当创建类的新对象时执行。
构造函数的名称与类的名称完全相同,它没有任何返回类型。
默认的构造函数没有任何参数。
但是如果你需要一个带有参数的构造函数可以有参数,这种构造函数叫做参数化构造函数。
这种技术可以帮助你在创建对象的同时给对象赋初始值
C# 中的析构函数
类的 析构函数 是类的一个特殊的成员函数,当类的对象超出范围时执行。
析构函数的名称是在类的名称前加上一个波浪形(~)作为前缀,它不返回值,也不带任何参数。
析构函数用于在结束程序(比如关闭文件、释放内存等)之前释放资源。
析构函数不能继承或重载。
C# 类的静态成员
我们可以使用 static 关键字把类成员定义为静态的。
当我们声明一个类成员为静态时,意味着无论有多少个类的对象被创建,只会有一个该静态成员的副本。
关键字 static 意味着类中只有一个该成员的实例。
静态变量用于定义常量,因为它们的值可以通过直接调用类而不需要创建类的实例来获取。
静态变量可在成员函数或类的定义外部进行初始化。
你也可以在类的定义内部初始化静态变量。
你也可以把一个成员函数声明为 static。
这样的函数只能访问静态变量。
静态函数在对象被创建之前就已经存在。
18. C# 继承
继承是面向对象程序设计中最重要的概念之一。
继承允许我们根据一个类来定义另一个类,这使得创建和维护应用程序变得更容易。
同时也有利于重用代码和节省开发时间。
当创建一个类时,程序员不需要完全重新编写新的数据成员和成员函数,只需要设计一个新的类,继承了已有的类的成员即可。
这个已有的类被称为的基类,这个新的类被称为派生类。
基类的初始化
派生类继承了基类的成员变量和成员方法。
因此父类对象应在子类对象创建之前被创建。
您可以在成员初始化列表中进行父类的初始化。
C# 多重继承
多重继承指的是一个类别可以同时从多于一个父类继承行为与特征的功能。
与单一继承相对,单一继承指一个类别只可以继承自一个父类。
C# 不支持多重继承。但是,您可以使用接口来实现多重继承
19. C# 多态性
多态性意味着有多重形式。
在面向对象编程范式中,多态性往往表现为"一个接口,多个功能"。
多态性可以是静态的或动态的。
在静态多态性中,函数的响应是在编译时发生的。
在动态多态性中,函数的响应是在运行时发生的。
静态多态性
在编译时,函数和对象的连接机制被称为早期绑定,也被称为静态绑定。
C# 提供了两种技术来实现静态多态性。分别为:
- 函数重载
- 运算符重载
我们讨论函数重载。
函数重载
您可以在同一个范围内对相同的函数名有多个定义。
函数的定义必须彼此不同,可以是参数列表中的参数类型不同,也可以是参数个数不同。
不能重载只有返回类型不同的函数声明。
实例演示几个相同的函数 print(),用于打印不同的数据类型.
动态多态性
C# 允许您使用关键字 abstract 创建抽象类,用于提供接口的部分类的实现。
当一个派生类继承自该抽象类时,实现即完成。
抽象类包含抽象方法,抽象方法可被派生类实现。
派生类具有更专业的功能。
请注意,下面是有关抽象类的一些规则:
- 您不能创建一个抽象类的实例。
- 您不能在一个抽象类外部声明一个抽象方法。
- 通过在类定义前面放置关键字 sealed,可以将类声明为密封类。当一个类被声明为 sealed 时,它不能被继承。抽象类不能被声明为 sealed。
当有一个定义在类中的函数需要在继承类中实现时,可以使用虚方法。
虚方法是使用关键字 virtual 声明的。
虚方法可以在不同的继承类中有不同的实现。
对虚方法的调用是在运行时发生的。
动态多态性是通过 抽象类 和 虚方法 实现的。
c#基础