C# 官方文档_C#语言介绍篇章
https://docs.microsoft.com/zh-cn/dotnet/csharp/tour-of-csharp/
C# 语言介绍
C#(读作“See Sharp”)是一种简单易用的新式编程语言,不仅面向对象,还类型安全。
C# 源于 C 语言系列,C、C++、Java 和 JavaScript 程序员很快就可以上手使用。
C# 是一种面向对象的语言。不仅如此,C# 还进一步支持面向组件的编程。
当代软件设计越来越依赖采用自描述的独立功能包形式的软件组件。
此类组件的关键特征包括:
1. 为编程模型提供属性、方法和事件;
2. 包含提供组件声明性信息的特性;
3. 包含自己的文档。
C# 提供了语言构造来直接支持这些概念,让 C# 成为一种非常自然的语言,可用于创建和使用软件组件。
多项 C# 功能有助于构造可靠耐用的应用程序:
1. 垃圾回收可自动回收无法访问的未使用对象占用的内存;
2. 异常处理提供了一种结构化的可扩展方法来执行错误检测和恢复;
3. C# 语言的类型安全设计禁止读取未初始化的变量、为范围之外的数组编制索引或执行未检查的类型转换。
C# 采用统一的类型系统。 所有 C# 类型(包括 int
和 double
等基元类型)均继承自一个根 object
类型。
因此,所有类型共用一组通用运算,任何类型的值都可以一致地进行存储、传输和处理。
此外,C# 还支持用户定义的引用类型和值类型,从而支持对象动态分配以及轻量级结构的内嵌式存储。
为了确保 C# 程序和库能够随着时间的推移以兼容的方式发展,C# 设计更强调版本控制。
许多编程语言很少关注这个问题,因此,当引入新版依赖库时,用这些语言编写的程序会出现更多不必要的中断现象。
由于更强调版本控制,直接受影响的 C# 设计方面包括:
单独的 virtual
和 override
修饰符、
关于方法重载决策的规则,
以及对显式接口成员声明的支持。
Hello world
“Hello, World”程序历来都用于介绍编程语言。
下面展示了此程序的 C# 代码:
using System;
class Hello
{
static void Main()
{
Console.WriteLine("Hello, Beyond");
}
}
C# 源文件的文件扩展名通常为 .cs
。
假设“Hello, World”程序存储在文件 hello.cs
中,则可以使用下列命令行编译此程序:
csc hello.cs
这会生成 hello.exe 可执行程序集。
运行此应用程序生成以下输出:
Hello, Beyond
重要注意事项:
编译 csc
命令实现的是完整框架,可能并不所有平台都适用。
“Hello, World”程序始于引用 System
命名空间的 using
指令。
命名空间提供了一种用于组织 C# 程序和库的分层方法。
命名空间包含类型和其他命名空间。
例如,System
命名空间包含许多类型(如程序中引用的 Console
类)和其他许多命名空间(如 IO
和 Collections
)。
借助引用给定命名空间的 using
指令,可以非限定的方式使用作为相应命名空间成员的类型。
由于使用 using
指令,因此程序可以使用 Console.WriteLine
作为 System.Console.WriteLine
的简写。
“Hello, World”程序声明的 Hello
类只有一个成员,即 Main
方法。
Main
方法是使用静态修饰符进行声明。
实例方法可以使用关键字 this
引用特定的封闭对象实例,
而静态方法则可以在不引用特定对象的情况下运行。
按照约定,Main
静态方法是程序的入口点。
程序的输出是由 System
命名空间中 Console
类的 WriteLine
方法生成。
此类由标准类库提供。默认情况下,编译器会自动引用标准类库。
关于 C#,要介绍的内容还有很多。
下面各主题概述了 C# 语言元素。
通过这些概述,可以了解 C# 语言所有元素的基本信息,并获得深入了解 C# 语言元素所需的信息:
-
程序结构
-
了解 C# 语言中的关键组织概念:程序、命名空间、类型、成员和程序集。
-
-
类型和变量
-
了解 C# 语言中的值类型、引用类型和变量。
-
-
表达式
-
表达式是在操作数和运算符的基础之上构造而成。 表达式生成的是值。
-
-
语句
-
语句用于表示程序的操作。
-
-
类和对象
-
类是最基本的 C# 类型。 对象是类实例。 类是使用成员生成的,此主题也对此进行了介绍。
-
-
结构
-
与类不同,结构是属于值类型的数据结构。
-
-
数组
-
数组是一种数据结构,其中包含许多通过计算索引访问的变量。
-
-
接口
-
接口定义了可由类和结构实现的协定。 接口可以包含方法、属性、事件和索引器。 接口不提供所定义的成员的实现代码,仅指定必须由实现接口的类或结构提供的成员。
-
-
枚举
-
枚举类型是包含一组已命名常量的独特值类型。
-
-
委托
-
委托类型表示对具有特定参数列表和返回类型的方法的引用。 通过委托,可以将方法视为可分配给变量并可作为参数传递的实体。 委托类似于其他一些语言中的函数指针概念,但与函数指针不同的是,委托不仅面向对象,还类型安全。
-
-
特性
-
使用特性,程序可以指定关于类型、成员和其他实体的附加声明性信息。
-
程序结构
C# 中的关键组织结构概念包括程序、命名空间、类型、成员和程序集。
C# 程序由一个或多个源文件组成。
程序声明类型,而类型则包含成员,并被整理到命名空间中。
类型示例包括类和接口。
成员示例包括字段、方法、属性和事件。
编译完的 C# 程序实际上会打包到程序集中。
程序集的文件扩展名通常为 .exe
或 .dll
,具体取决于实现的是应用程序还是库。
以下示例在 Acme.Collections
命名空间中声明 Stack
类:
using System;
namespace Acme.Collections
{
public class Stack
{
Entry top;
public void Push(object data)
{
top = new Entry(top, data);
}
public object Pop()
{
if (top == null)
{
throw new InvalidOperationException();
}
object result = top.data;
top = top.next;
return result;
}
class Entry
{
public Entry next;
public object data;
public Entry(Entry next, object data)
{
this.next = next;
this.data = data;
}
}
}
}
此类的完全限定的名称为 Acme.Collections.Stack
。
此类包含多个成员:一个 top
字段、两个方法(Push
和 Pop
)和一个 Entry
嵌套类。
Entry
类还包含三个成员:一个 next
字段、一个 data
字段和一个构造函数。
假定示例的源代码存储在 acme.cs
文件中,以下命令行
csc /t:library acme.cs
将示例编译成库(不含 Main
入口点的代码),并生成 acme.dll
程序集。
重要注意事项:
上述示例使用 csc
作为命令行 C# 编译器。
此编译器是 Windows 可执行文件。
若要在其他平台上使用 C#,应使用 .NET Core 工具。 .
NET Core 生态系统使用 dotnet
CLI 来管理命令行生成。
这包括管理依赖项和调用 C# 编译器。
有关在 .NET Core 支持的平台上使用这些工具的完整说明,请参阅这篇教程。
程序集包含中间语言 (IL) 指令形式的可执行代码和元数据形式的符号信息。
执行前,程序集中的 IL 代码会被 .NET 公共语言运行时的实时 (JIT) 编译器自动转换成处理器专属代码。
由于程序集是包含代码和元数据的自描述功能单元,因此无需在 C# 中使用 #include
指令和头文件。
只需在编译程序时引用特定的程序集,即可在 C# 程序中使用此程序集中包含的公共类型和成员。
例如,此程序使用 acme.dll
程序集中的 Acme.Collections.Stack
类:
using System;
using Acme.Collections;
class Example
{
static void Main()
{
Stack s = new Stack();
s.Push(1);
s.Push(10);
s.Push(100);
Console.WriteLine(s.Pop());
Console.WriteLine(s.Pop());
Console.WriteLine(s.Pop());
}
}
如果程序存储在文件 example.cs
中,
那么在 example.cs
编译完后,可以使用编译器的 /r 选项引用 acme.dll 程序集:
csc /r:acme.dll example.cs
这会创建 example.exe
可执行程序集,它将在运行时输出以下内容:
100
10
1
使用 C#,可以将程序的源文本存储在多个源文件中。
编译多文件 C# 程序时,可以将所有源文件一起处理,并且源文件可以随意相互引用。
从概念上讲,就像是所有源文件在处理前被集中到一个大文件中一样。
在 C# 中,永远都不需要使用前向声明,因为声明顺序无关紧要(除了极少数的例外情况)。
C# 并不限制源文件只能声明一种公共类型,也不要求源文件的文件名必须与其中声明的类型相匹配。
类型和变量
C# 有两种类型:值类型和引用类型。
值类型的变量直接包含数据,而引用类型的变量则存储对数据(称为“对象”)的引用。
对于引用类型,两个变量可以引用同一对象;
因此,对一个变量执行的运算可能会影响另一个变量引用的对象。
借助值类型,每个变量都有自己的数据副本;
因此,对一个变量执行的运算不会影响另一个变量(ref
和 out
参数变量除外)。
C# 值类型又细分为简单类型、枚举类型、结构类型和可以为 null 的值类型。
C# 引用类型又细分为类类型、接口类型、数组类型和委托类型。
下面概述了 C# 的类型系统。
- 值类型
- 简单类型
- 有符号的整型:
sbyte
、short
、int
、long
- 无符号的整型:
byte
、ushort
、uint
、ulong
- Unicode 字符:
char
- IEEE 浮点:
float
、double
- 高精度小数:
decimal
- 布尔:
bool
- 有符号的整型:
- 枚举类型
- 格式为
enum E {...}
的用户定义类型
- 格式为
- 结构类型
- 格式为
struct S {...}
的用户定义类型
- 格式为
- 可以为 null 的值类型
- 值为
null
的其他所有值类型的扩展
- 值为
- 简单类型
- 引用类型
- 类类型
- 其他所有类型的最终基类:
object
- Unicode 字符串:
string
- 格式为
class C {...}
的用户定义类型
- 其他所有类型的最终基类:
- 接口类型
- 格式为
interface I {...}
的用户定义类型
- 格式为
- 数组类型
- 一维和多维,例如
int[]
和int[,]
- 一维和多维,例如
- 委托类型
- 格式为
delegate int D(...)
的用户定义类型
- 格式为
- 类类型
八个整型类型支持带符号或不带符号格式的 8 位、16 位、32 位和 64 位值。
两个浮点类型(float
和 double
)分别使用 32 位单精度和 64 位双精度 IEC-60559 格式表示。
decimal
类型是适用于财务和货币计算的 128 位数据类型。
C# 的 bool
类型用于表示布尔值(true
或 false
)。
C# 使用 Unicode 编码处理字符和字符串。 char
类型表示 UTF-16 代码单元,string
类型表示一系列 UTF-16 代码单元。
下面总结了 C# 的数值类型。
- 有符号的整型
sbyte
:8 位,介于 -128 到 127 之间short
:16 位,介于 -32,768 到 32,767 之间int
:32 位,介于 -2,147,483,648 到 2,147,483,647 之间long
:64 位,介于 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 之间
- 无符号的整型
byte
:8 位,介于 0 到 255 之间ushort
:16 位,介于 0 到 65,535 之间uint
:32 位,介于 0 到 4,294,967,295 之间ulong
:64 位,介于 0 到 18,446,744,073,709,551,615 之间
- 浮点
float
:32 位,介于 1.5 × 10-45 到 3.4 × 1038 之间,7 位精度double
:64 位,介于 5.0 × 10-324 到 1.7 × 10308 之间,15 位精度
- 十进制
decimal
:128 位,至少介于 -7.9 × 10-28 到 7.9 × 1028 之间,至少为 28 位精度
C# 程序使用类型声明创建新类型。
类型声明指定新类型的名称和成员。
用户可定义以下五种 C# 类型:类类型、结构类型、接口类型、枚举类型和委托类型。
class
类型定义包含数据成员(字段)和函数成员(方法、属性及其他)的数据结构。
类类型支持单一继承和多形性,即派生类可以扩展和专门针对基类的机制。
struct
类型定义包含数据成员和函数成员的结构,这一点与类类型相似。
不过,与类不同的是,结构是值类型,通常不需要进行堆分配。
结构类型不支持用户指定的继承,并且所有结构类型均隐式继承自类型 object
。
interface
类型将协定定义为一组已命名的公共函数成员。
实现 interface
的 class
或 struct
必须提供接口函数成员的实现代码。
interface
可以继承自多个基接口,class
和 struct
可以实现多个接口。
delegate
类型表示引用包含特定参数列表和返回类型的方法。
通过委托,可以将方法视为可分配给变量并可作为参数传递的实体。
委托类同于函数式语言提供的函数类型。
委托也类似于其他一些语言中的函数指针概念,
但与函数指针不同的是,委托不仅面向对象,还类型安全。
class
、struct
、interface
和 delegate
类型全部都支持泛型,因此可以使用其他类型对它们进行参数化。
enum
类型是一种包含已命名常量的独特类型。
每个 enum
类型都有一个基础类型(必须是八种整型类型之一)。
enum
类型的值集与基础类型的值集相同。
C# 支持任意类型的一维和多维数组。 与上述类型不同,数组类型无需先声明即可使用。
相反,数组类型是通过在类型名称后面添加方括号构造而成。
例如,int[]
是 int
类型的一维数组,
int[,]
是 int
类型的二维数组,
int[][]
是由 int
类型的一维数组构成的一维数组。
可以为 null 的值类型也无需先声明即可使用。
对于所有不可以为 null 的值类型 T
,都有对应的可以为 null 的值类型 T?
,后者可以包含附加值 null
。
例如,int?
是可以包含任何 32 位整数或值 null
的类型。
C# 采用统一的类型系统,因此任意类型的值都可视为 object
。
每种 C# 类型都直接或间接地派生自 object
类类型,而 object
是所有类型的最终基类。
只需将值视为类型 object
,即可将引用类型的值视为对象。
通过执行装箱和取消装箱操作,可以将值类型的值视为对象。
在以下示例中,int
值被转换成 object
,然后又恢复成 int
。
using System;
class BoxingExample
{
static void Main()
{
int i = 123;
object o = i; // Boxing
int j = (int)o; // Unboxing
}
}
当值类型的值转换成 object
类型时,将分配 object
实例(亦称为“箱”)来包含值,然后该值会复制到相应的箱中。
相反,当 object
引用被显式转换成值类型时,将检查引用的 object
是否是具有正确值类型的箱;
如果检查成功,则会将箱中的值复制出来。
C# 的统一类型系统实际上意味着可以“按需”将值类型转换成对象。
鉴于这种统一性,使用类型 object
的常规用途库可以与引用类型和值类型结合使用。
C# 有多种变量,其中包括字段、数组元素、局部变量和参数。
变量表示存储位置,每个变量都具有一种类型,用于确定可以在变量中存储哪些值,如下文所述。
- 不可以为 null 的值类型
- 具有精确类型的值
- 可以为 null 的值类型
null
值或具有精确类型的值
- object
null
引用、对任意引用类型的对象的引用,或对任意值类型的装箱值的引用
- 类类型
null
引用、对类类型实例的引用,或对派生自类类型的类实例的引用
- 接口类型
null
引用、对实现接口类型的类类型实例的引用,或对实现接口类型的值类型的装箱值的引用
- 数组类型
null
引用、对数组类型实例的引用,或对兼容的数组类型实例的引用
- 委托类型
null
引用或对兼容的委托类型实例的引用
表达式
表达式是在操作数和运算符的基础之上构造而成。
表达式的运算符指明了向操作数应用的运算。
运算符的示例包括 +
、-
、*
、/
和 new
。
操作数的示例包括文本、字段、局部变量和表达式。
如果表达式包含多个运算符,那么运算符的优先级决定了各个运算符的计算顺序。
例如,表达式 x + y * z
相当于计算 x + (y * z)
,因为 *
运算符的优先级高于 +
运算符。
如果操作数两边的两个运算符的优先级相同,那么运算符的结合性决定了运算的执行顺序:
- 除了赋值运算符之外,所有二元运算符均为左结合运算符,即从左向右执行运算。 例如,
x + y + z
将计算为(x + y) + z
。 - 赋值运算符和条件运算符 (
?:
) 为右结合运算符,即从右向左执行运算。 例如,x = y = z
将计算为x = (y = z)
。
可以使用括号控制优先级和结合性。
例如,x + y * z
先计算 y
乘 z
,并将结果与 x
相加,
而 (x + y) * z
则先计算 x
加 y
,然后将结果与 z
相乘。
大多数运算符都可以重载。
借助运算符重载,可以为一个或两个操作数为用户定义类或结构类型的运算指定用户定义运算符实现代码。
下面总结了 C# 运算符,按优先级从高到低的顺序列出了各类运算符。
同一类别的运算符的优先级也相同。
每个类别下均列出了相应类别的表达式,以及对每种表达式类型的说明。
- 基本
x.m
:成员访问x(...)
:方法和委托调用x[...]
:数组和索引器访问x++
:后置递增x--
:后置递减new T(...)
:创建对象和委托new T(...){...}
:使用初始值设定项的对象创建new {...}
:匿名对象初始值设定项new T[...]
:数组创建typeof(T)
:获取T
的 Type 对象checked(x)
:在已检查的上下文中计算表达式unchecked(x)
:在未检查的上下文中计算表达式default(T)
:获取类型为T
的默认值delegate {...}
:匿名函数(匿名方法)
- 一元
+x
:标识-x
:取反!x
:逻辑取反~x
:按位取反++x
:前置递增--x
:前置递减(T)x
:将x
显式转换成类型T
await x
:异步等待x
完成
- 乘法
x * y
:乘法x / y
:除法x % y
:求余
- 加法
x + y
:加法、字符串串联、委托组合x – y
:减法、委托删除
- Shift
x << y
:左移位x >> y
:右移位
- 关系和类型测试
x < y
:小于x > y
:大于x <= y
:小于或等于x >= y
:大于或等于x is T
:如果x
是T
,返回true
;否则,返回false
x as T
:返回类型为T
的x
;如果x
的类型不是T
,返回null
- 相等
x == y
:等于x != y
:不等于
- 逻辑“与”
x & y
:整数型位AND,布尔型逻辑 AND
- 逻辑 XOR
x ^ y
:整数型位 XOR,布尔型逻辑 XOR
- 逻辑“或”
x | y
:整数型位 OR,布尔型逻辑 OR
- 条件“与”
x && y
:仅当x
不是false
时,才计算y
- 条件“或”
x || y