今天我们将继续学习 C# 这门编程语言里的一些基本概念。我们将围绕前面的程序代码为大家作介绍。

  1. using System;
  2. namespace Project
  3. {
  4. internal class Program
  5. {
  6. private static void Main()
  7. {
  8. Console.WriteLine("Hello, world!");
  9. }
  10. }
  11. }

如果你是完全没有学过 C# 的话,所有的概念可能对你来说,理解上有些吃力。一方面,我推荐你先学会一些基本的 C 语言概念后,再来学习 C# 的语法;另外一方面,我也会尽量把内容讲得很清楚,避免 C 语言学习后继续学 C# 的时候发生知识点关联和衔接不上的问题。

当然,如果这么讲内容的话,可能部分读者会觉得多少有一些啰嗦。一方面,请见谅;另外一方面,很多人的编程水平是参差不齐的,有些学习得很好的朋友,对编程很熟悉;概念基本上都不用怎么学就可以写代码了;而有些朋友可能初学编程,对概念很不熟悉,所以得慢慢来。为了保证尽量多的朋友能够学会 C#,所有的内容可能会讲很细,直到绝大多数朋友都能接受,因此不要嫌啰嗦。

Part 1 关键字(Keyword)

请注意代码的第一行。using 在 C# 里是一个关键字(Keyword)。所谓的关键字,就是在说,“C# 里,我使用这个词语来表示特殊用途”。

从另外一方面来讲,这一行的内容必须写成 using ...; 的形式,其中的 using 单词不能写成 useused 甚至 user。关键字是永久不变的(不论是单词本身,还是单词的变形,都是不变的),而且每一个关键字都对应了语法和语义的规则,且不会变动,因此在学习的时候,只需要掌握每一个关键字的如下两点即可:

  • 这个关键字怎么写;
  • 这个关键字的用法到底如何。

就可以了。以后只要看到这样的写法,你就可以立即回忆出它的用法是什么,这就是学习关键字应该有的感觉。

顺带一提,所有 C# 的关键字(这里的 using 只是其中一个关键字)都是全小写的。只有一个关键字比较特殊,它不是一个单词(写成一个下划线:_),不过这一点我们会在后面的“弃元”文章内容里作介绍。

Part 2 引用指令(Using Directive)

下面我们来说明一下,什么是一个引用指令。以 using 起头的这个形式,我们称为引用指令。它一般都写在文件的最开头。当然,你这么写也可以:

  1. namespace Project
  2. {
  3. using System;
  4. internal class Program
  5. {
  6. private static void Main()
  7. {
  8. Console.WriteLine("Hello, world!");
  9. }
  10. }
  11. }

即写在 namespace 用大括号包裹起来的里面。两种写法基本没区别,只是写在外面可能更好看一点。比如说代码里的 using System; 就称为一条引用指令。using 前面说了,它是一个关键字,而后面的 System 则不是。这个 System 称为命名空间(Namespace)。命名空间指的是一个完整的项目里,规划归纳内容或者功能究竟属于哪一个类型的一种机制。system 这个单词的意思是系统,也就是说,using System; 的意思是在说,我需要引入系统自带的东西到这个项目里来。

在代码的第 9 行,我们用到了 Console.WriteLine,它表示“把固定内容打到屏幕上显示”的这么一个功能。计算机里把这个过程叫做输出(Output)。因为这个功能是我们上手写代码就可以用的功能,所以是系统自带的功能,因此,在使用这句话的时候,你需要在最开头写上 using System; 来保证 Console.WriteLine 是系统自带的过程。

命名空间的架构使用“名称.名称.名称”的方式来表达。你可以回忆一下在学习生物的时候,我们把动植物分成界门纲目科属种七个层级。这里的命名空间的概念和这个层级关系是类似的:System 是最开头的级别,也就是最大级别。它包含了很多详细的子类别,比如说 System.Numerics(写 using System.Numerics; 就表示你可以使用系统自带的数据处理的功能)、System.Text(写 System.Text; 就表示你可以使用系统自带的处理文字信息的功能)等等。

命名空间采用的是固定的大写字母开头的英文单词或词组,在系统里包含了非常多的命名空间可提供你使用。单词问题来了:既然你说单词是固定的,但是你刚才说的 Numerics 啊、Text 这些,我完全都不知道,这是从哪里看的呢?命名空间是需要你先提前记住一些的。而最需要记住的就是这里的 System。其它的其实并不重要,而你只需要先知道 System 就行了。在以后我们讲到其它的内容的时候,我们会用到别的命名空间的时候才会继续作出说明。

总之,命名空间就是用来规划项目里所有功能用的,它用来划分区分功能。

Part 3 自定义命名空间

请注意 namespace Project 这一行文字。这一行就是在定义命名空间。我们使用大括号来区分和划分定义命名空间的范围。比如这个代码里,从第 3 行开始到第 9 行结束,都属于 Project 这个命名空间的范畴。

  1. namespace Project
  2. {
  3. internal class Program
  4. {
  5. private static void Main()
  6. {
  7. Console.WriteLine("Hello, world!");
  8. }
  9. }
  10. }

因为入门我们还用不上 using Project; 类似的、引用自定义命名空间的指令,因此只需要知道一点,命名空间是通过 namespace 这一行文字来规定的。是的,namespace 也是 C# 里的一个关键字。它的功能就是用来定义一个命名空间。这里你记住这么用就可以了,知道它就是一个规定命名空间的一个意思就可以了。

Part 4 主方法(Main Method)

另外一个 C# 比较重要的概念就是主方法。主方法是整个程序开始运行、跑起来的入口。换句话说,只要我们写了 private static void Main() 的话,那么就是在说明,“整个程序都从这里开始从上到下依次执行功能”。比如这个代码里:

  1. using System;
  2. namespace Project
  3. {
  4. internal class Program
  5. {
  6. private static void Main()
  7. {
  8. Console.WriteLine("Hello, world!");
  9. }
  10. }
  11. }

使用了大括号来表示,主方法从第 8 行开始,到第 10 行结束,整个这一部分都属于主方法的范围。而大括号本身没有实际的执行意义,所以真正在执行功能的只有第 9 行。

主方法的头部(Signature,也叫签名)的所有可能写法可以总结成如下的内容:

  1. 主方法签名
  2. 访问修饰符? 'static' 类型 'Main' '(' 参数? ')'
  3. 访问修饰符
  4. 'public'
  5. 'private'
  6. 参数
  7. 'string[] args'
  8. 类型
  9. 'void'
  10. 'int'

这个写法你可能需要了解一下。以后我们在说明语法的格式的时候,会广泛采用这种写法。下面我们就来说明一下,这个记号的书写规则。

Part 5 语法格式的书写约定

其中,我们把我们要描述的一个语法使用中文名(或者英文单词)来表示,写在左上角;然后把它的格式,写在下面。比如主方法的语法格式就是 访问修饰符? 'static' 类型 'Main' '(' 参数? ')'

接着,在这个写法里用到了“访问修饰符”、“类型”、“参数”等使用中文名表达的中间元素。这些元素都可以在下面的内容里找到写法。比如说“访问修饰符”可以使用 public 或者 private,选择其中一个写法,填入到前面主方法格式的“访问修饰符”的对应位置上。旁边带有的问号 ? 称为通配符。所有这种描述下的通配符一共有三种:

  • 问号(?):表示前面的这个内容可以有,也可以没有。有的话,最多只能有一个;
  • 星号(*):表示前面的这个内容可以有,也可以没有,而且还不限定出现次数;
  • 加号(+):表示前面的这个内容必须有,且至少有一个;
  • 没有记号:必须有,且只能有一个。

举个例子。“访问修饰符?”可以写 publicprivate、甚至不写出来,但不能写 private public 或者别的,因为这样的话,算作出现两次访问修饰符了。

然后是单引号的内容。单引号里的内容是固定不变的内容。换句话说,这个语法格式下,这里就必须写成这个单词或者词组,且不能修改大小写。

那么,主方法的这么长的语法格式就可以被理解成:先写一个访问修饰符(privatepublic,也可以不写),然后是 static 单词,然后是类型(voidint 里选择一个),然后是 Main 单词,然后是开小括号,然后是参数(可以写,写的话必须写成 string[] args,也可以不写),然后是闭小括号。

我们可以利用这个格式,来表述引用指令的语法格式:

  1. 引用指令
  2. 'using' 名称 ';'

其中,“名称”是随意的英文单词,所以没有固定的书写格式,因此这里就省略“名称”的具体书写格式了。而命名空间定义的格式如下:

  1. 命名空间定义
  2. 'namespace' 命名空间名称 '{' 所有可以属于该命名空间的内容 '}'

其中 '{' 所有可以属于该命名空间的内容 '}' 就对应了代码的这一段:

  1. {
  2. internal class Program
  3. {
  4. private static void Main()
  5. {
  6. Console.WriteLine("Hello, world!");
  7. }
  8. }
  9. }

所以,这个格式,你看明白了吗?那么,语法格式的约定我们就说到这里。

我其实并不是很像让大家学习这个东西。初学来说,学习这个东西有些麻烦了。但是因为先掌握这个,以后描述很多东西都会很轻松,所以这一点先介绍也不是坏事。

Part 6 标识符(Identifier)

标识符是一个我们可以直接写到代码里,且是我们自定义的词语、编译器也不管你的符号。在上一节的代码里,SystemProgramMain 均属于标识符。

可能你会问我,SystemMain 这些单词不是固定的吗?是的,但你改成别的东西之后,编译器并不会认为有问题;相反,其它依赖于这些符号的地方才会产生错误信息,但本身 SystemMain 这些符号并不会影响什么。这些我们称为标识符。标识的识读作 zhì,即和“标志”读音一致。

1-1 标识符命名规则

为了编译器可以识别和认定一个自定义的词语是一个标识符,我们有如下的约定:

  1. 由数字、字母、下划线、@ 符号、中文字符、日语、韩语等常见语言字符构成。
  2. 第一个字符不能是数字,但可以是字母和下划线,且还可以是原义符号 @
  3. 当你想使用关键字的写法作为标识符的时候,需要在单词前面追加原义符号@,其它的情况,原义符号则可有可无。原义符号只能有一个,且只能放在最开始。
  4. 大小写敏感。

这个规定我不知道你能否理解。比如说,Program 就是一个正确的标识符、Main 也是、System 也是。另外,在 C# 里,namespace 是一个关键字,但你又确实想把 namespace 作为标识符来使用的话,需要追加原义符号,即改写成 @namespace,编译器就知道这个是一个叫 namespace 的标识符了。

写成语法格式就是:

  1. 标识符格式
  2. '@'? 开头字符 普通字符*
  3. 开头字符
  4. 字母
  5. 下划线
  6. 普通字符
  7. 数字
  8. 字母
  9. 下划线

标识符可以用在自定义的命名空间上、方法名上、类名上,等等其它的地方。你现在学到的程序,由于没有数据类型,因此无法详细介绍。下一节我们就会进入数据类型这一节内容;那么那个时候,你就可以使用自定义的标识符作为变量来使用了。

1-2 标识符命名规范

前面说到了命名规则,它们是强制性的规定,必须要满足的。下面我们来说一下软性规定。这种规定不需要你一定满足,但希望你满足。

标识符有四种基本的命名规则:

  • 驼峰命名法(camelCase)
  • 帕斯卡命名法(PascalCase)
  • 匈牙利命名法(typeCase)
  • 蛇命名法(snake_case)

其中匈牙利命名法需要配合类型作为介绍,因此我们这里就不作说明了。

驼峰命名法是说,如果一个标识符需要多个有意义的单词构成的话,第一个单词的首字母使用小写,而其它单词全部首字母大写,剩下的字符均使用小写的一种写法,比如说 checkWhetherThatGirlIsBeautiful。第一个单词 check 的首字母是小写的,而后续的单词,首字母全部是大写的。

帕斯卡命名法和驼峰命名法只差一点:第一个单词的首字母也得大写。所以帕斯卡命名法的话,前面这个标识符就得写成 CheckWhetherThatGirlIsBeautiful

最后,蛇命名法需要把所有单词全部小写,然后单词之间用下划线分隔,那么就得是 check_whether_that_girl_is_beautiful 这样的。

在 C# 里,我们一般对命名空间、方法名使用帕斯卡命名法。也就是说,我们一般是见不到小写开头的命名空间名称,也一般见不到小写开头的方法名。前面的 Main 方法(我们叫主方法)的 M 也是大写的。学习过 C 语言的朋友应当知道,C 语言里的 main 函数(主函数)是小写起头的。C# 是大写起头,因此需要注意。

标识符在 C# 里起着重要的意义。标识符是大小写敏感(Case Sensitive)的,因而 main 和 Main 是两个不同的标识符。

Part 7 缩进(Indenting)

缩进并非是 C# 首创。C 语言里,缩进就已经存在了。缩进指的是代码里没有执行意义的那些空格。举个例子:

  1. using System;
  2. namespace Project
  3. {
  4. internal class Program
  5. {
  6. private static void Main()
  7. {
  8. Console.WriteLine("Hello, world!");
  9. }
  10. }
  11. }

在这段代码里,第 5 行(internal class Program)前面有 4 个空格。为了保证代码层级关系很清晰,我们写代码需要添加这样的空白来保证代码更好看。比如上面这样写代码,我们就可以清晰地知道,namespace Project 包含了 Program 以及里面的这些东西;而 internal class Program 则又包含了 Main 以及里面的东西;Main 方法则又包含了里面的执行逻辑(输出一行文字到屏幕上)。

缩进在划分层级关系的时候相当有用。空白字符是不起到执行意义的,但对我们理解代码来说,非常有意义。

一般来说,缩进分空格(Space)和制表符(Tab)两种,比如例子里,缩进就采用了空格作为缩进字符。你也可以用制表符,不过并不建议你混用两种字符类型,因为空格和制表符都是“看不见”的,混用可能会导致代码出现隐藏的问题。

制表符就是你键盘上左起第一列里写了“Tab”字样的按键。按下这个按键,就会自动插入一个制表符。一个制表符的长度相当于 8 个空格;但在代码里,一般相当于 4 个空格的长度。所以如果插入空格的话,需要按 4 下空格按键,这样就可以插入 4 个空格,和一个制表符长度相同。

Part 8 注释(Comment)

可以从英文名里面看出,注释用的是一个叫做 comment 的单词。comment 原本的意思是评论(当然,现如今很多日本朋友也会把 comment 理解成弹幕的意思,虽然日语的 danmaku 也是弹幕)。这里我们使用“评论”这层释义。

我们可以对代码里的任何地方插入注释。注释里可以写上任何我们想写的话,来作为这行代码(或这段代码)的解释说明或者是评论。举个例子:

  1. using System; // The 'using' statement.
  2. /*
  3. The namespace statement. In addition, this statement is unncessary.
  4. In other words, you don't need to write this.
  5. */
  6. namespace Project
  7. {
  8. /*
  9. The class declaration, which is too complex to introduce.
  10. Therefore, I won't introduce about this now.
  11. */
  12. /**
  13. * <summary>
  14. * Provides main method of this program.
  15. * </summary>
  16. */
  17. internal class Program
  18. {
  19. /// <summary>
  20. /// The main entry point of this program.
  21. /// </summary>
  22. private static void Main()
  23. {
  24. // Output a line of text onto the screen.
  25. Console.WriteLine("Hello, world!");
  26. }
  27. }
  28. }

其中,所有以 ///// 开头的部分,全部称为注释。C# 的注释一共有四种:

  • 单行注释(// 开头);
  • 多行注释(以 /* 开头,以 */ 结尾,这两个东西可以不在同一行。这两个东西包裹起来的所有部分都是注释);
  • 经典文档注释(/** 开头,以 */ 结尾,和多行注释用法完全一样,只是把开头的 /* 改成 /** 而已);
  • C# 专用文档注释(/// 开头的单行注释文字)。

单行注释以 // 起头,只要写了这个起头的标记,后面的所有东西都会被认为是注释文字。可以从示例里看出,注释文字是随便写的,不需要遵循任何 C# 代码的语法规则,它完全可以是一句英语,甚至写中文也是可以的。这就是注释的一大特殊用途:注释文字是随便写什么都可以的,因为编译器知道是注释,因而它们并不参与编译和程序的运行

而多行注释和单行注释用法完全一样,也是起到补充说明代码的具体逻辑用的。只是用到了 /**/ 作为定界符,因此从这一点来说,它和单行注释不一样。

一般来说,我们都不怎么用多行注释;相反,单行注释用得很多。即使是占据多行文字,我们也会每一行都加上 // 来写单行注释,这样整体看起来就是一个多行注释了:

  1. // The namespace statement.
  2. // In addition, this statement is unncessary;
  3. // in other words, you don't need to write this.
  4. namespace Project
  5. {
  6. }

比如这样。

文档注释我们会在“类”的内容里讲到,这里就不说明了(对于我们现在的内容来说,也没有什么特殊的意义),因为这里的 summary 这些单词是固定的,但它们都有特殊意义,因此在这里说明会显得很臃肿。

Part 10 总结

本文我们初步了解了 C# 代码的基本构造,只有 internal class Program 我们在前面没有讲了。这个叫做(Class),因为内容很繁琐,所以你当成固定的文本记住就行。到时候我们讲到类的时候,会对这个写法作出详细说明。

我们了解到,整个程序的大体构造是“引用指令”、“命名空间声明”、“类”、“主方法”以及执行逻辑,是按照顺序写的,这就是我们本节需要让大家掌握的内容。下一节我们会继续对 Console 等概念作出补充说明。

我们把基本的 C# 概念全部给大家介绍了一通。我不知道你能接受多少,但它们在后续的内容里我们还会提到。如果不怎么记得的话,还可以回来看,所以不必担心今天的内容的难度多大。

实际上,今天就作为理论学习一下就可以,因此不必带有负担。