目录
3.1 C#的基本语法
与其他语言(如Python)的编译器不同,C#编译器不考虑代码中的空格、回车符或制表符(这些字符统称为空白字符)。这样格式化代码时就有很大的自由度,但遵循某些规则将有助于提高代码的可读性。
C#代码由一系列语句组成,每条语句都用一个分号结束。因为空白被忽略,所以一行可以有多条语句,但从可读性的角度看,通常在分号的后面加上回车符,不在一行中放置多条语句。但一条语句放在多行是可以的(也比较常见)。
C#是一种块结构的语言,所有语句都是代码块的一部分。这些块用花括号来界定(“{”和“}”),代码块可以包含任意多行语句,或者根本不包含语句。注意花括号字符不需要附带分号。
例如,简单的C#代码块如下所示:
{
<code line 1, statement 1>;
<code line 2, statement 2>
<code line 3, statement 2>;
}
其中<code line x , statement y >部分并非真正的C#代码,而是用这个文本作为C#语句的占位符。在这段代码中,第2、第3行代码是同一条语句的一部分,因为在第2行的末尾没有分号。缩进第3行代码,就更容易确定这是第二行代码的继续。
下面的简单示例还使用了缩进格式,提高了C#代码的可读性。这是标准做法,实际上在默认情况下VS会自动缩进代码。一般情况下,每个代码块都有自己的缩进级别,即它向右缩进了多少。代码块可以互相嵌套(即块中可以包含其他块),而被嵌套的块要缩进得多一些。
{
<code line 1>;
{
<code line 2>;
<code line 3>;
}
<code line 4>;
}
注意: 在能通过Tools|Options访问的VS Options对话框中,显示了VS用于格式化代码的规则。在Text Editor|C#|Formatting节点的子类别下,包含了其中很多规则。此处的大多数设置都反映了还没有讲述的C#部分,但如果以后要修改设置,以更适合自己的个性化样式,就可以回过头来看看这些设置。在本书中,为简洁起见,所有代码段都使用默认设置来格式化。
在C#代码中,另一种常见的语句是注释。注释并非严格意义上的C#代码,但代码最好有注释。注释的作用不言自明:给代码添加描述性文本(用英语、法语、德语、外蒙古语等),编译器会忽略这些内容。在开始处理冗长的代码段时,注释可用于为正在进行的工作添加提示。
单行注释 //开头,该行为注释内容
块注释 /*开头,*/结尾,中间部分的块为注释内容
文本注释 ///开头,连续的多行文本注释
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
//这是单行注释
///文本注释第1行
///文本注释第2行
///文本注释第3行
/*块注释第1行
* 块注释第2行
* 块注释末行
*/
}
注意:
///会被编译,
//不会被编译
所以使用///会减慢编译的速度(但不会影响执行速度)
///会在其它的人调用你的代码时提供智能感知
3.2 C#控制台应用程序的基本结构
举个例子
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _3._2_CSharp控制台应用程序的基本结构
{
class Program
{
static void Main(string[] args)
{
//output text to the screen.
Console.WriteLine("The first app in Beginning C");
Console.ReadKey();
}
}
}
代码结构
小技巧
以#开头的任意关键字实际上是一个预处理指令,严格地说并不是C#关键字。
使用#region和#endregion关键字来定义可以展开和折叠的代码区域的开头和结尾。
namespace _3._2_CSharp控制台应用程序的基本结构
{
class Program
{
static void Main(string[] args)
{
//output text to the screen.
Console.WriteLine("The first app in Beginning C");
Console.ReadKey();
}
#region
static void Main2(string[] args)
{
//output text to the screen.
Console.WriteLine("The first app in Beginning C");
Console.ReadKey();
}
#endregion
}
}
3.3 变量
要使用变量,需要声明它们,即给变量指定名称和类型。声明变量后,就可以把它们用作存储单元,存储所声明的数据类型的数据。
声明变量的C#语法是指定类型和变量名,如下所示:
<type><name>;
如果使用未声明的变量,代码将无法编译。
此时编译器会告诉我们出现了什么问题,所以这不是一个灾难性错误。另外,使用未赋值的变量也会产生一个错误,编译器会检测出这个错误。
3.3.1 简单类型
简单类型就是组成应用程序中基本构件的类型。
(1)整数类型
序号 | 类型 | 别名 | 说明 | 允许的值 |
1 | sbyte | System.SByte | 8位有符号整数 | 介于–128和127之间的整数 |
2 | byte | System.Byte | 8位无符号整数 | 介于0和255之间的整数 |
3 | short | System.Int16 | 16位有符号整数 | 介于–32 768和32 767之间的整数 |
4 | ushort | System.UInt16 | 16位无符号整数 | 介于0和65 535之间的整数 |
5 | int | System.Int32 | 32位有符号整数 | 介于–2 147 483 648和2 147 483 647之间的整数 |
6 | uint | System.UInt32 | 32位无符号整数 | 介于0和4 294 967 295之间的整数 |
7 | long | System.Int64 | 64位有符号整数 | 介于–9 223 372 036 854 775 808和9 223 372 036 854 775 807之间的整数 |
8 | ulong | System.UInt64 | 64位无符号整数 | 介于0和18 446 744 073 709 551 615之间的整数 |
(2)浮点类型
序号 | 类型 | 别名 | 说明 | 允许的值 |
1 | float | System.Single | 32位单精度浮点数 | |
2 | double | System.Double | 64位双精度浮点数 | |
3 |
(3)字符和布尔类型
序号 | 类型 | 别名 | 允许的值 |
1 | char | System.Char | 一个Unicode字符,存储0和65 535之间的整数 |
2 | bool | System.Boolean | 布尔值:true或false |
(4)引用类型
序号 | 类型 | 别名 | 允许的值 |
1 | string | System.String | 一组字符 |
2 | object | System.Object | 根类型,CTS中其他类型都是从它派生而来的,包括值类型 |
例子
class Program
{
static void Main(string[] args)
{
//声明两个变量
int myInteger;
string myString;
//给两个变量赋值
myInteger = 17;
myString = "\"myInteger\" is";
Console.WriteLine($"{myString} {myInteger}");
Console.ReadKey();
}
}
运行结果
"myInteger" is 17
3.3.2 变量的命名
基本的变量命名规则如下:
变量名的第一个字符必须是字母、下划线(_)或@。
其后的字符可以是字母、下划线或数字。
变量名是正确的:
myBigVar
VAR1
_test
变量名有误:
99BottlesOfBeer
namespace
It's-All-Over
3.3.3 字面值
(1)字面值
类型 | 类别 | 后缀 | 示例/允许的值 |
bool | 布尔 | 无 | true或false |
int、uint、long、ulong | 整数 | 无 | 100 |
uint、ulong | 整数 | u或U | 100U |
long、ulong | 整数 | l或L | 100L |
ulong | 整数 | ul、uL、Ul、UL、lu、lU、Lu或LU | 100UL |
float | 实数 | f或F | 1.5F |
double | 实数 | 无、 d或D | 1.5 |
decimal | 实数 | m或M | 1.5M |
char | 字符 | 无 | 'a'或转义序列 |
string | 字符串 | 无 | "a...a",可以包含转义序列 |
(2)转义值
转义序列 | 产生的字符 | 字符的Unicode值 |
\' | 单引号 | 0x0027 |
\" | 双引号 | 0x0022 |
\\ | 反斜杠 | 0x005C |
\0 | null | 0x0000 |
\a | 警告(产生蜂鸣) | 0x0007 |
\b | 退格 | 0x0008 |
\f | 换页 | 0x000C |
\n | 换行 | 0x000A |
\r | 回车 | 0x000D |
\t | 水平制表符 | 0x0009 |
\v | 垂直制表符 | 0x000B |
下面的字符串是等价的:
"Benjamin\'s string."
"Benjamin\u0027s string."
一字不变的字符串在文件名中非常有用,因为文件名中大量使用了反斜杠字符。如果使用一般字符串,就必须在字符串中使用两个反斜杠,例如:
"C:\\Temp\\MyDir\\MyFile.doc"
而有了一字不变的字符串字面值,这段代码就更便于阅读。下面的字符串与上面的等价:
@"C:\Temp\MyDir\MyFile.doc"
3.4 表达式
把变量和字面值(在使用运算符时,它们都称为操作数)与运算符组合起来,就可以创建表达式,它是计算的基本构件。
运算符大致分为如下3类:
- 一元运算符,处理一个操作数
- 二元运算符,处理两个操作数
- 三元运算符,处理三个操作数
3.4.1 数学运算符
(1)简单的数学运算符
运算符 | 类别 | 示例表达式 | 结果 |
+ | 二元 | var1=var2+var3; | var1的值是var2与var3的和 |
- | 二元 | var1=var2-var3; | var1的值是从var2减去var3所得的值 |
* | 二元 | var1=var2* var3; | var1的值是var2与var3的乘积 |
/ | 二元 | var1=var2/var3; | var1是var2除以var3所得的值 |
% | 二元 | var1=var2 % var3; | var1是var2除以var3所得的余数 |
+ | 一元 | var1=+var2; | var1的值等于var2的值 |
- | 一元 | var1=-var2; | var1的值等于var2的值乘以–1 |
(2)字符串连接运算符
运算符 | 类别 | 示例表达式 | 结果 |
+ | 二元 | var1=var2+var3; | var1的值是存储在var2和var3中的两个字符串的连接值 |
(3)简单表达式结果
运算符 | 类别 | 示例表达式 | 结果 |
++ | 一元 | var1=++var2; | var1的值是var2+1,var2递增1 |
–– | 一元 | var1=– –var2; | var1的值是var2– 1,var2递减1 |
++ | 一元 | var1=var2++; | var1的值是var2,var2递增1 |
–– | 一元 | var1=var2– –; | var1的值是var2,var2递减1 |
例子
class Program
{
static void Main(string[] args)
{
//定义变量:2个整数,1个字符串类型
int firstNumber, secondNumber;
string userName;
//界面引导,输入姓名,欢迎用户
Console.WriteLine("Enter your name:");
userName = Console.ReadLine();
Console.WriteLine($"Welcome {userName}!");
//提示获取2个数,并转换成双精度浮点型
Console.WriteLine("Now give me a integer:");
firstNumber = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("Now give me another integer:");
secondNumber = Convert.ToInt32(Console.ReadLine());
//方法一:两个数的加减乘除余
Console.WriteLine("The sum of {0} and {1} is " + "{2}.", firstNumber, secondNumber, firstNumber + secondNumber);
Console.WriteLine("The result of subtracting {0} from " + "{1} is {2}.", secondNumber, firstNumber, firstNumber - secondNumber);
Console.WriteLine("The product of {0} and {1} " + "is {2}.", firstNumber, secondNumber, firstNumber * secondNumber);
Console.WriteLine("The result of dividing {0} by " + "{1} is {2}.", firstNumber, secondNumber, firstNumber / secondNumber);
Console.WriteLine("The remainder after dividing {0} by " + "{1} is {2}.", firstNumber, secondNumber, firstNumber % secondNumber);
Console.ReadKey();
//方法二:两个数的加减乘除余
Console.WriteLine($"The sum of {firstNumber} and {secondNumber} is " + $"{firstNumber + secondNumber}.");
Console.WriteLine($"The result of subtracting {secondNumber} from " + $"{firstNumber} is {firstNumber - secondNumber}.");
Console.WriteLine($"The product of {firstNumber} and {secondNumber} " + $"is {firstNumber * secondNumber}.");
Console.WriteLine($"The result of dividing {firstNumber} by " + $"{secondNumber} is {firstNumber / secondNumber}.");
Console.WriteLine($"The remainder after dividing {firstNumber} by " + $"{secondNumber} is {firstNumber % secondNumber}.");
//Console.ReadKey();
}
}
运行结果
Enter your name:
tony
Welcome tony!
Now give me a integer:
4
Now give me another integer:
3
The sum of 4 and 3 is 7.
The result of subtracting 3 from 4 is 1.
The product of 4 and 3 is 12.
The result of dividing 4 by 3 is 1.
注意,表达式firstNumber+secondNumber等,作为Console.WriteLine()语句的一个参数,而没有使用中间变量:
Console.WriteLine($"The sum of {firstNumber} and {secondNumber} is " + $"{firstNumber + secondNumber}.");
这种语法可以提高代码的可读性,并减少需要编写的代码量。
3.4.2 赋值运算符
赋值运算符
运算符 | 类别 | 示例表达式 | 结果 |
= | 二元 | var1=var2; | var1被赋予var2的值 |
+= | 二元 | var1+=var2; | var1被赋予var1与var2的和 |
-= | 二元 | var1-=var2; | var1被赋予var1与var2的差 |
*= | 二元 | var1*=var2; | var1被赋予var1与var2的乘积 |
/= | 二元 | var1/=var2; | var1被赋予var1与var2相除所得的结果 |
%= | 二元 | var1 %=var2; | var1被赋予var1与var2相除所得的余数 |
3.4.3 运算符的优先级
优先级 | 运算符 |
优先级由高到低 | ++、--(用作前缀)、+、- |
*、/、% | |
+、- | |
=、*=、/=、%=、+=、-= | |
++、--(用作后缀) |
3.4.4 名称空间
名称空间是.NET中提供应用程序代码容器的方式,这样就可以唯一地标识代码及其内容。
名称空间也用作.NET Framework中给项分类的一种方式,大多数项都是类型定义。
默认情况下,C#代码包含在全局名称空间中。这意味着对于包含在这段代码中的项,全局名称空间中的其他代码只要通过名称进行引用,就可以访问它们。可使用namespace关键字为花括号中的代码块显式定义名称空间。如果在该名称空间代码的外部使用名称空间中的名称,就必须写出该名称空间中的限定名称。
namespace LevelOne
{
// code in LevelOne namespace
// name "NameOne" defined
}
// code in global namespace
注意:这段代码定义了一个名称空间LevelOne,以及该名称空间中的一个名称NameOne(注意这里在应该定义名称空间的地方添加了一个注释,而没有列出实际代码,这是为了使我们的讨论更具普遍性)。在名称空间LevelOne中编写的代码可以直接使用NameOne来引用该名称,但全局名称空间中的代码必须使用限定名称LevelOne.NameOne来引用这个名称。
3.5 练习
(1)在下面的代码中,如何从名称空间fabulous的代码中引用名称great?
namespace fabulous
{
// code in fabulous namespace
}
namespace super
{
namespace smashing
{
// great name defined
}
}
答案:super.smashing.great()
(2)下面哪些变量名不合法?
myVariableIsGood
99Flake //以数字开头,不合法
_floor
time2GetJiggyWidIt
wrox.com //包含·,不合法
(3)字符串"supercalifragilisticexpialidocious"是不是太长了,不能放在string变量中?如果是,原因是什么?
答案:string为应用类型变量,其真正对象存储在char中。string本身是不限制长度的,可以存放任意长度的字符串。
(4)考虑运算符的优先级,列出下述表达式的计算步骤:
resultVar += var1 * var2 + var3 % var4 / var5;
答案:
①计算var1*var2;
②计算var3%var4;
③计算(var3%var4)/var5;
④计算(var1 * var2) + (var3 % var4 / var5)
⑤计算resultVar+[(var1 * var2) + (var3 % var4 / var5)],并将值赋给resultVar;
(5)编写一个控制台应用程序,要求用户输入4个int值,并显示它们的乘积。提示:前面看到可以使用Convert.ToDouble()命令把用户在控制台上输入的数转换为double类型;类似地,从string类型转换为int类型的命令是Convert.ToInt32()。
static void Main(string[] args)
{
Console.WriteLine("请输入第1个数字:");
double a = Convert.ToDouble(Console.ReadLine());
Console.WriteLine("请输入第2个数字:");
double b = Convert.ToDouble(Console.ReadLine());
Console.WriteLine("请输入第3个数字:");
double c = Convert.ToDouble(Console.ReadLine());
Console.WriteLine("请输入第4个数字:");
double d = Convert.ToDouble(Console.ReadLine());
Console.Write("以上四个数字的乘积为:");
Console.WriteLine($"{a}*{b}*{c}*{d}={a*b*c*d}");
//Console.WriteLine("{0}*{1}*{2}*{3}={4}",a,b,c,d,a*b*c*d);
Console.ReadKey();
}
3.6 本章要点
序号 | 主题 | 要点 |
1 | C#基本语法 | C#是一种区分大小写的语言,每行代码都以分号结束。如果代码行太长或者想要标识嵌套的块,可以缩进代码行,以方便阅读。使用//或/*…*/语法可以包含不编译的注释。代码块可以隐藏到区域中,也是为了方便阅读 |
2 | 变量 | 变量是有名称和类型的数据块。.NET Framework定义了大量简单类型,例如数字和字符串(文本)类型,以供使用。变量只有经过声明和初始化后,才能使用。可以把字面值赋予变量,以初始化它们,变量还可在单个步骤中声明和初始化 |
3 | 表达式 | 表达式利用运算符和操作数来建立,其中运算符对操作数执行操作。运算符有3种:一元、二元和三元运算符,它们分别操作1、2和3个操作数。数学运算符对数值执行操作,赋值运算符把表达式的结果放在变量中。运算符有固定的优先级,优先级确定了运算符在表达式中的处理顺序 |
4 | 名称空间 | .NET应用程序中定义的所有名称,包括变量名,都包含在名称空间中。名称空间采用层次结构,我们通常需要根据包含名称的名称空间来限定名称,以便访问它们 |