c# 核心技术指南——第2章 c# 语言基础

本书中几乎所有的程序和代码片段都可以作为交互式示例在 LINQPad 中运行。阅读本书时使用这些示例可以加快你的学习进度。在 LINQPad 中编辑执行这些示例可以立即得到结果,无须在 VisualStudio 中建立项目和解决方案。

2.1 第一个 C# 程序

在 C# 中,语句按顺序执行,每个语句都以分号结尾。类将函数成员和数据成员聚合在一起形成面向对象的构建单元。Console 类将处理命令行的输入输出功能聚合在一起,例如 WriteLine 方法。类是一种类型,我们会在 2.3 节进行介绍。

可以使用 using 指令导入命名空间来避免烦冗的代码:

c# 复制代码
using System;

int x = 12 * 30;
Console.WriteLine(x);

一系列语句被成对的大括号包围起来,称为语句块(statement block)。

方法是 C# 中的诸多种类函数之一。另一种函数是我们用来执行乘法运算的 * 运算符。其他的函数种类还包括构造器、属性、事件、索引器和终结器。

编译

C# 编译器能够将一系列 .cs 为扩展名的源代码文件编译成程序集,程序集是 .NET 中工单打包和部署单元。程序集可以是一个应用程序也可以是一个库。

普通的控制台程序或 Windows 应用程序包含一个入口点(entry point),而库则没有。库可以被应用程序或其他的库调用(引用)​。.NET 5 就是由一系列库(及运行时环境)组成的。

上一节中的每一个程序都是直接由一系列语句(称为顶级语句)开头的。当存在顶级语句时,控制台程序或 Windows 应用程序将隐式创建入口点(若没有顶级语句,则 Main 方法将作为应用程序的入口点------请参见 2.3.2 节)

与 .NET Framework 不同,.NET 6 程序集并没有 .exe 扩展名。.NET 6 应用程序构建之后生成的 .exe 文件只是一个负责启动 .dll 程序集的原生加载器。这个 .exe 文件是和平台相关的。

.NET 5 能够创建自包含部署程序,它包含加载器、程序集以及 .NET 运行时本身。而以上内容均包含在一个单一 .exe 文件中。

dotnet 工具(在 Windows 下则为 dotnet.exe)是一个用于管理 .NET 源代码和二进制文件的命令行工具。该工具可以像集成开发环境(例如 VisualStudio 和 Visual Studio Code)那样构建或启动程序。

dotnet 工具可通过安装 .NET 5 SDK 或安装 Visual Studio获得,其默认安装位置在 Windows 操作系统上位于 %ProgramFiles%/dotnet,在 UbuntuLinux 上位于 /usr/bin/dotnet。

dotnet 工具在编译应用程序时需要指定一个工程文件(project file)及一个或者多个 C# 代码文件。以下命令将创建一个控制台应用程序的基本结构:

cmd 复制代码
dotnet new Console -n MyFirstProgram

上述命令将创建名为 MyFirstProgram 的子目录,并在其中创建名为 MyFirstProgram.csproj 的工程文件,以及包含 Main 方法的 Program.cs 代码文件,其中 Main 方法将在控制台输出"Hello World"​。

在 MyFirstProgram 目录执行以下命令将构建并启动上述应用程序:

cmd 复制代码
dotnet run MyFirstProgram

如果仅仅希望构建应用程序,但不执行,则可以执行以下命令:

cmd 复制代码
dotnet build MyfirstProgram.csproj

构建生成的程序集将保存在 bin/debug 子目录下。

我们将在第 17 章详细介绍程序集。

2.2 语法

C# 的语法基于 C 和 C++ 语法。

2.2.1 标识符和关键字

标识符是程序员为类、方法、变量等选择的名字。

C# 标识符是区分大小写的,通常约定参数、局部变量以及私有字段应该以小写字母开头(例如 myVariable),而其他类型的标识符则应该以大写字母开头(例如 MyMethod)。

2.2.2 字面量、标点与运算符

字面量在语法上是嵌入程序中的原始数据片段。

2.2.3 注释

C#提供了两种不同形式的源代码文档:单行注释和多行注释。多行注释由/*开始,由*/结束。

2.3 类型基础

本书的大多数代码需要使用 System 命名空间下的类型。因此除了展示与命名空间相关的概念,后面的示例我们将忽略"using System"语句。

变量表示一个存储位置,其中的值可能会不断变化。与之对应,常量总是表示同一个值。C#中的所有值都是某一种类型的实例。

2.3.1 预定义类型示例

预定义类型是指那些由编译器特别支持的类型,例如 int。

在 C# 中,预定义类型(也称为内置类型)拥有相应的C#关键字。在 .NET 的 System 命名空间下也包含了很多不是预定义类型的重要类型(例如 DateTime)。

2.3.2 自定义类型(Class)

2.3.2.1 类型成员

类型包含数据成员和函数成员。

2.3.2.2 预定义类型和自定义类型

C# 的优点之一是其中的预定义类型和自定义类型非常相近。

2.3.2.3 构造器和实例化

构造器的定义类似于方法,不同的是它的方法名和返回类型是合并在一起的,并且其名称为所属的类型名称。

2.3.2.4 实例与静态成员

默认情况下,成员就是实例成员。

不对类型实例进行操作的数据成员和函数成员可以标记为 static(静态)。

事实上,Console 类是一个静态类,即它的所有成员都是静态的,并且该类型无法实例化。

c# 复制代码
public class Panda
{
    public string Name; // Instance field

    public static int Population; // Static field

    public Panda(string n)
    {
        Name = n;
        Population = Population + 1;
    }
}

如果试图求p1.Population或者Panda.Name的值,则会产生编译时错误。

2.3.2.5 public 关键字

public 关键字将成员公开给其他类,如果字段没有标记为公有(public)的,那么它就是私有的。

2.3.2.6 定义命名空间

命名空间是组织类型的有效手段,对于大型程序尤为如此。

2.3.2.7 定义 Main 方法

到目前为止,本书的范例均使用了顶级语句(顶级语句是C# 9引入的特性)。

如不使用顶级语句,C# 将查找静态 Main 方法,并将这个方法作为程序入口点。Main 方法可以定义在任何类中(并且只能够存在一个 Main 方法)。如果 Main 方法需要访问特定类型的私有成员,则可以将 Main 方法定义在相应类中。这种做法要比顶级语句更简单。

Main 方法可以返回一个整数(而非 void)。该整数将返回到执行环境中(一般非零值代表失败)。Main 方法也可以接受一个字符串数组作为参数(该数组将包含所有传递给可执行程序的参数)。

Main方法也可以声明为async方法,并返回Task或者Task以支持异步编程。我们将在第14章介绍该内容。

顶级语句(C#9)

C# 9 引入的顶级语句可以避免静态 Main 方法及包含该方法的类型。具备顶级语句的文件由以下三部分组成:

  • using 指令(可选)
  • 一系列语句,其中也可以包含方法的声明
  • 类型与命名空间声明(可选)

例如:

c# 复制代码
using System;                       // Part 1

Console.WriteLine("Hello, world");  // Part 2
void SomeMethod1() {  }             // Part 3
Console.WriteLine("Hello again!");  // Part 4
void SomeMethod2() {  }             // Part 5

class SomeClass {  }                // Part 6
namespace SomeNamespace { }         // Part 7

由于 CLR 并不显式支持顶级语句,因此编译器会将上述代码转换为类似以下形式:

c# 复制代码
using System;               // Part 1

static class Program$       // Special compiler-generated name
{
    static void Main$ (string[] args)
    {
        Console.WriteLine("Hello, world");  // Part 2
        void SomeMethod1() {  }             // Part 3
        Console.WriteLine("Hello again!");  // Part 4
        void SomeMethod2() {  }             // Part 5
    }
}

class SomeClass {  }                // Part 6
namespace SomeNamespace { }         // Part 7

请注意,第 2 部分(Part 2)是包裹在主方法中的。这意味着 SomeMethod1 和 SomeMethod2 都是局部方法。我们将会在 3.1.3.2 节中进行完整介绍。而目前最重要的是局部方法(非 static 的声明)可以访问声明在父级方法中的变量:

c# 复制代码
int x = 3;
LocalMethod();

void LocalMehtod() { Console.WriteLine(x); } // we can access x

这种方式的其他后果就是顶级方法无法从其他类或类型中访问。

顶级语句可以将整数返回给调用者(并非必需),并可以"神奇地"访问 string[] 类型的 args 参数,以对应调用者从命令行中传递给程序的参数。

由于每一个应用程序只可能拥有一个入口,因此在 C# 项目中最多只能在一个文件里使用顶级语句。

2.3.3 类型和转换

转换始终会根据一个已经存在的值创建一个新的值

隐式转换只有在以下条件都满足时才能进行:

  • 编译器确保转换总能成功。
  • 没有信息在转换过程中丢失。

相对地,只有在满足下列条件时才需要显式转换:

  • 编译器不能保证转换总是成功。
  • 信息在转换过程中有可能丢失。

如果编译器可以确定某个转换必定失败,那么这两种转换都无法执行。包含泛型的转换在特定情况下也会失败,请参见 3.9.11 节。

以上的数值转换是 C# 中内置的。C# 还支持引用转换、装箱转换(参见第 3 章)与自定义转换(参见 4.17 节)。对于自定义转换,编译器并没有强制满足上述规则,因此没有良好设计的类型有可能在转换时产生意想不到的效果。

2.3.4 值类型与引用类型

相关推荐
邓熙榆4 分钟前
Logo语言的网络编程
开发语言·后端·golang
S-X-S1 小时前
项目集成ELK
java·开发语言·elk
Johaden2 小时前
EXCEL+Python搞定数据处理(第一部分:Python入门-第2章:开发环境)
开发语言·vscode·python·conda·excel
ByteBlossom6666 小时前
MDX语言的语法糖
开发语言·后端·golang
肖田变强不变秃7 小时前
C++实现矩阵Matrix类 实现基本运算
开发语言·c++·matlab·矩阵·有限元·ansys
沈霁晨7 小时前
Ruby语言的Web开发
开发语言·后端·golang
小兜全糖(xdqt)7 小时前
python中单例模式
开发语言·python·单例模式
DanceDonkey7 小时前
@RabbitListener处理重试机制完成后的异常捕获
开发语言·后端·ruby
Python数据分析与机器学习7 小时前
python高级加密算法AES对信息进行加密和解密
开发语言·python
军训猫猫头7 小时前
52.this.DataContext = new UserViewModel(); C#例子 WPF例子
开发语言·c#·wpf