# 变量和表达式

# 基本语法

C#由一系列语句组成,每条语句都有一个分号结束。C#是一种块结构的语言,块使用花括号来界定,不像Python用缩进界定,代码块可包含任意多行语句。

在C#代码中,有两种注释,代码注释和xml注释。

# 代码注释


//块注释:
/*
comment
...
*/

//行注释:
// comment

1
2
3
4
5
6
7
8
9
10

# XML注释

参考资料:XML文档注释 (opens new window)

可以通过这个方式为代码创建文档:将特殊注释字段中的XML元素包含在源代码中注释引用的代码块前面,使用-doc选项进行编译时,编译器会在源代码中搜索所有XML标记,并创建一个XML文档文件,若要基于编译器生成的文件创建最终文档,可以使用DocFX (opens new window)Sandcastle (opens new window)

//单行分隔符
///

//多行分隔符
/** <summary>text</summary> */  

/**  
<summary>text</summary>  
*/  

/**  
 * <summary>text</summary>  
*/  
1
2
3
4
5
6
7
8
9
10
11
12
13

C#代码区分大小写,下面的代码都不能正常工作:

console.WriteLine("hello world");
Console.Writeline("hello world");
1
2

# 控制台应用基本结构

using System;
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLien("hello world");
            Console.ReadKey();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

# 预处理指令

同C++一样,C#也有很多预处理指令,比如#region和#endregion,详情请看C# 预处理器指令 (opens new window)

# 变量

假如把计算机内存比作成一个架子,那么变量就是架子上的盒子,而数值字符等就是盒子中的内容,因为有很多类型的数值,如果放在一种类型的盒子中,比如较小类型的数值放在较大类型数值所在的盒子中,会浪费空间,这就产生了很多种变量类型,也就说有很多种盒子。

声明C#的变量语法:

type name;

# 简单类型

简单类型是组成应用程序中的基本构件类型,比如数值和布尔值,同复杂类型不一样,简单类型没有子类型或特性。

C#中的每一种类型都利用了 .Net Framework中定义的标准类型,使用标准类型可以在语言之间交互操作,在C#中这些类型的名称是Framework中定义的类型的别名。下面来看下它们的类型:

# 整数类型

类型 别名 允许的值
sbyte System.SByte -2^7 ~ 2^7-1
byte System.Byte 0 ~ 2^8-1
short System.Int16 -2^15 ~ 2^15-1
ushort System.UInt16 0 ~ 2^16-1
int System.Int32 -2^31 ~ 2^31-1
uint System.UInt32 0 ~ 2^32-1
long System.Int64 -2^63 ~ 2^63-1
ulong System.UInt64 0 ~ 2^64-1

"u"是unsigned的缩写,表示不能在这些类型的变量中存储负数,这样就把符号位用作数值存储位,比有符号数多存储一倍。

# 浮点类型

浮点类型可以用 m*2^e形式来表示:

类型 别名 m的最小值 m的最大值 e的最小值 e的最大值 近似值的最小值 近似值的最大值
float System.Single 0 2^24 -149 104 1.5*10^-45 3.4*10^38
double System.Double 0 2^53 -1075 970 5*10^-324 1.7*10^308
decimal System.Decimal 0 2^96 -28 0 1*10^-28 7.9*10^28

# 文本和布尔类型

类型 别名 允许的值
char System.Char 一个Unicode字符,存储0~2^16-1直接的数
bool System.Boolean 布尔值:true或false
string System.String 一个字符序列

string的字符数量是没有上限的,因为它可以使用可变大小的内存。

# Nullable类型

在 .Net 2.0中,C#提供了一种Nullable类型,允许定义值类型包含null值,这对于处理数据库可空字段有很大的帮助,具体就是在定义变量时类型名后面添加?。

还提供了??运算符,当被操作的变量为空值时,表达式返回??后面的值,可以利用这点给变量初始化。

static void Main(string[] strs)
{
    int? intVal = null;
    Console.WriteLine($"intVal.HasValue:{intVal.HasValue}");
    int intVal2 = intVal ?? -1;
    Console.WriteLine($"intVal2:{intVal2}");
    int intVal3 = intVal.GetValueOrDefault();
    Console.WriteLine($"intVal3:{intVal3}");

    //Console.WriteLine($"intVal.HasValue:{intVal.Value}");
    intVal = 2;
    Console.WriteLine($"intVal.HasValue:{intVal.HasValue}");
}
1
2
3
4
5
6
7
8
9
10
11
12
13

注意:

  • 语法T?是System.Nullable<T>的简写,此处T为值类型,这两种形式可以互换。
  • 如果变量已被赋值,则Value属性返回值,如果没有被赋值,其值为null值,则抛出System.InvalidOperationException异常。
# Nullable运算符
public class Person
{
    public int Age;
    public int GetAge()
    {
        return Age;
    }
}
static void Main(string[] args)
{
    int? org=1;
    int result = (int)org * 2;
    Console.WriteLine(result);

    //上面没有显式转换为int的话是编译不过去的。如果org为null,会抛出System.InvalidOperationException异常。
    int? org2 = 2;
    int result2 = org2.Value * 2;
    Console.WriteLine(result2);

    int? org3 = 3;
    int? result3 = org3 * 2;
    Console.WriteLine(result3.Value);

    bool? bool1 = true;
    bool? bool2 = false;
    //bool1* bool2;

    int? org4 = null;
    int result4 = org4 * 2 ?? 5;
    Console.WriteLine(result4);

    Person p1 = null ;
    Console.WriteLine(p1?.GetAge());

    //var onChanged = onChanged;
    //if (onChanged != null)
    //{
    //    onChanged(this, args);
    //}

    //上面检查事件是否为空的代码可用下面一行表示。

    //onChanged?.Invoke(this, args);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

注意:

  • 可空类型赋值给非可空类型是需要显式转换的。
  • ??运算符为空接合运算符(null coalescing operator),是一个二元运算符,允许给可能等于null的表达式提供另一个值。
  • ?.运算符通常称为Elvis运算符或空条件运算符,有助于避免繁杂的空值检查造成的代码歧义,它还可用于检查事件是否为空。

# 变量的命名

基本的命名规则如下:

  • 变量名的第一个字符必须是字母,下划线_或@
  • 其后的字符可以是字母,下划线或数字。

# 字面值

有很多后缀可以添加在值的后面来表示值的类型:

类型 类别 后缀 示例
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",或包含转义序列

上面整数的排列顺序意为解析的优先级,比如一个没有后缀的整数会根据大小先被解析为int,如果超出范围解析为uint,还超出范围解析为long,次之为ulong。

# 整数

整数分为二进制,八进制,十六进制,他们的前缀依次为0b,0,0x。

当字面值为二进制时阅读很费劲,C#提出了数字分隔符,它还可用于十进制,浮点数和双精度,就是使用一个下划线作为分割符:

float one = 3.14_15_926_3;
int two = 0b0002_0001;
1
2

# 字符串字面值

有一些字符在前面加\后会有一些其他含义,这些字符可能会直接表示特殊数据或某些功能。

转义序列 产生的字符 字符的Unicode值
' 单引号 0x0027
" 双引号 0x0022
\ 反斜杠 0x005C
\0 null 0x0000
\a 警告有蜂鸣声 0x0007
\b 退格 0x0008
\f 换页 0x000C
\n 换行 0x000A
\r 回车 0x000D
\t 水平制表符 0x0009
\v 垂直制表符 0x000B

可以直接使用转义序列或者前缀'\u'加上十六进制的Unicode值,下面两种形式是等价的:

"Hello\nWorld"
"Hello\u000AWorld"
1
2

当一个字符串中有很多转义字符的时候,比如路径需要很多的反斜杠,如果此时要把所有反斜杠转义会很麻烦,C#中可以在字符串前面加@符号,让所有的字符都不转义,这样会方便一点。

@"hello
world"

@"C:\temp\list\myfile.txt"
1
2
3
4

# 表达式

将变量和字面量与运算符结合起来,就可以创建表达式,它是计算的基本构件。它有两大类:一个是数学运算符,除了加,减,乘,除,取余,还有自增自减。其中"+"能用于字符串连接;另一类是赋值运算符,即"=","+=","-="等。

# 布尔逻辑

布尔类型有true或false两种,布尔比较需要使用布尔比较运算符,它们有==,!=,<,>,<=,>=。布尔比较直接返回布尔类型:

bool isTrue;
isTrue = myBool == true;
1
2

有四个条件布尔运算符,就是&,|,&&,||。&&和||同&和|的差别就是如果第一个条件能判断出结果,就不会比较第二个条件。比如&&运算符,如果第一个条件为false,那么它直接返回false。

# 布尔赋值运算符

布尔类型有三个赋值运算符,分别是&=,|=和^=。&=和|=赋值运算符并不是&&和||条件布尔运算符,而是使用&和|。

# 运算符优先级

运算符
++和--(前缀),+(一元),-(一元),(),!,~
*,/,%
+(二元),-(二元)
<<,>>
<,>,<=,=>
==,!=
&
^
|
&&
||
=,*=,/=,%=,+=,-=,<<=,=>>,&=,^=,|=
++和--(后缀)

这些运算符从上往下优先级依次降低。