c#笔记-流程控制语句

随机数

为了展示流程控制的作用,需要每次执行程序时都使用不一样的值。
无论是每次都修改源码,还是从控制台读取输入都太麻烦了。
所以使用随机数进行测试。

随机数的使用方式如下,使用随机数类,获取一个默认的随机数工具,然后用这个工具生成随机数。1

int r1 = Random.Shared.Next();//在0到int上限中随机
int r2 = Random.Shared.Next(10);//从0到这个值之间随机,不会取到上限值
int r3 = Random.Shared.Next(20, 30);//从20到30之间随机,不会取到30
double d1 = Random.Shared.NextDouble();//获取一个0到1之间的小数,永远不会取到1

变量储存值而不储存操作。这里的作用是把一个随机数的值储存到一个变量中。
除非修改这个变量的值,否则他会一直保持这个值。并不会每次访问都会发生变化。

条件

条件在大部分情况下都由bool类型来担任。
他只有true(真,成立,正确)和false(假)两个值。
除了直接声明变量外,还可以使用运算符进行运算。例如大于和小于。

bool b1= 1 > 2;

选择

if

if选择接收一个条件,如果条件成立才会执行

int r4 = Random.Shared.Next(100);
if (r4 > 80)
{
	Console.WriteLine("触发暴击");
}
Console.WriteLine("随机数是" + r4);

所有流程控制语句中,如果跟随一个大括号,那么语句跟大括号之间不要用分号隔开

if-else if

在if的语句块后可以跟随else if语句。
当前面的if没有判中,就会由后面的else if处理。
else if后还能继续接else if,直到你不想接为止。
此外,最后一个else可以省略if,表示不用再判定,必定执行。

int r5 = Random.Shared.Next(100);
if (r5 > 98)
{
	Console.WriteLine("评分为SS");
}
else if (r5 > 95)
{
	Console.WriteLine("评分为S");
}
else if (r5 > 90)
{
	Console.WriteLine("评分为A");
}
else if (r5 > 80)
{
	Console.WriteLine("评分为B");
}
else if (r5 > 60)
{
	Console.WriteLine("评分为C");
}
else
{
	Console.WriteLine("未通过");
}

Console.WriteLine("随机数是" + r5);

这一串判断都是对同一个值进行,不能使用Random.Shared.Next(100)代替所有的r5

switch

switch选择是用于配合模式匹配的选择。
它的使用场景类似于长if-else if,但更整洁。

int r5 = Random.Shared.Next(100);
switch (r5)//switch只会取值一次,这里可以不声明变量直接写Random.Shared.Next(100)
{
	case > 98:
		Console.WriteLine("评分为SS");
		break;

	case > 95:
		Console.WriteLine("评分为S");
		break;

	case > 90:
		Console.WriteLine("评分为A");
		break;

	case > 80:
		Console.WriteLine("评分为B");
		break;

	case > 60:
		Console.WriteLine("评分为C");
		break;

	default:
		Console.WriteLine("未通过");
		break;
}
  • 每一个case分支类似于if的条件。
  • default类似于无条件的else。
  • 每个分支的结尾都需要加上break来结束switch。
    除非一个分支后紧跟着另一个分支,中间没有任何执行语句。
switch (Random.Shared.Next(1, 13))
{
	case 1:
	case 3:
	case 5:
	case 7:
	case 8:
	case 10:
	case 12:
		Console.WriteLine("这月有31天");
		break;

	case 2:
		Console.WriteLine("这月有28天");
		break;

	default:
		Console.WriteLine("这月有30天");
		break;
}

但是现在有了模式匹配,可以在一个case中进行多个值的判断,这种联合判断已经没有意义了。

swtich的主判断只能使用模式匹配,而模式匹配只能使用常量。
switch的次判断可以使用变量,但即便只想使用次判断,也需要加一个无意义的主判断。
在主要判断后加上when来启动次要判断。

int r6 = Random.Shared.Next(100);
int r7 = Random.Shared.Next(100);

switch (true)
{
	case true when r6 > r7:
		Console.WriteLine("r6更大");
		break;

	case true when r6 < r7:
		Console.WriteLine("r7更大");
		break;
}

循环

while

类似于if,条件满足才会执行。
但是,在执行完毕以后,会再判断条件,并决定是否再次执行。
直到条件不满足。

int r8 = Random.Shared.Next(100);
while (r8 > 4)
{
	Console.WriteLine("没有抽中SSR");
	r8 = Random.Shared.Next(100);
}
Console.WriteLine("抽中了SSR");

do-while

do-while循环的条件判断在循环结束而不是循环开始。
和普通while的区别在于,进入循环的时候没有条件判断,所以一定会执行第一次循环。
而普通while可能第一次条件就判断失败,完全不执行。而之后的流程是一样的。

string? s1;
do
{
	Console.WriteLine("初次启动或密码错误,请输入密码");
	s1 = Console.ReadLine();
} while (s1 == "123456");
Console.WriteLine("登录成功");

do-while循环的使用场景偏向于类似输入密码的场合,判断条件是根据执行内容判断,所以至少要执行一次才能判断。
而这种场合也是声明变量是分离声明和赋值的场合。在这里使用var并赋值初始值,初始值是会被覆盖的。

do-while的条件后面是不跟大括号的,所以while后面是要加分号的。

for

for循环有3个部分,临时变量声明条件判断迭代语句
for循环可以理解为东西更多的while循环。不过如果不需要临时变量声明迭代语句,那么while循环更加精简。

for (int i = 0; i < 10; i++)
{
	Console.WriteLine("第" + i + "次丢骰子,丢出来" + Random.Shared.Next(1, 7));
}

for循环括号内的三个部分都可以留空,只要保证存在两个分号就不会报错。

  • 条件部分留空会视为true,始终通过。
  • 临时变量声明部分可以省略,也可以声明多个同类型变量。
    但是因为语法问题,这里不能同时声明不同类型的变量。
  • 迭代部分和循环体的差别,是迭代部分不会与跳转语句continue交互。除此之外没有区别。

for循环的执行顺序是:临时变量声明->条件判断->循环体->迭代部分->条件判断->循环体->迭代部分

跳转

break

break用来中断当前的循环,或用于switch内终止switch。

for (int i = 1; i < 10; i++)
{
	for (int j = 1; j < 10; j++)
	{
		Console.Write($"{i} * {j} = {i * j}\t");
		if (j > i - 1)
		{
			break;
		}
	}
	Console.WriteLine();
}

可以调整循环条件和if来达到相同的效果。
但如果存在多层if嵌套,多条件判断。那么使用break可能更清晰和简单。

continue

continue用来重启当前循环。
重启循环会忽略循环的剩余部分,直接回到条件判断。
但对于for循环,会先进入他的迭代部分,再进行判断。

int ssr = 0;

for (int i = 0; i < 100; i++)
{
	int number = Random.Shared.Next(1, 101);
	if (number > 4)
	{
		Console.WriteLine("抽到r");
		continue;
	}
	Console.WriteLine("抽中了ssr!");
	ssr = ssr + 1;
}

Console.WriteLine($"你抽到的ssr有" + ssr);

goto

break和continue的缺点是,只能控制当前所在的switch或循环。
goto语句可以做到更精细的跳转,包括跳出多层循环。

for (int i = 1; i <= 10; i++)
{
	switch (i)
	{
		case 1:
			Console.WriteLine("One");
			break;

		case 2:
			Console.WriteLine("Two");
			break;

		case 3:
			Console.WriteLine("Three");
			break;

		case 4:
			Console.WriteLine("Four");
			goto End; //跳转到End 标签处
		default:
			Console.WriteLine("Other");
			break;
	}
}
End://定义一个标签,用于goto语句的跳转
Console.WriteLine("结束");

使用goto首先要定义一个标签,并且这个标签后面必须再跟随一条语句。
也就是说goto不能直接结束代码。

如果swich分支使用的条件是纯常量,没有使用模式匹配,
则可以以分支作为目标跳转。

int num = 3;

switch (num)
{
    case 1:
        Console.WriteLine("这是第一个 case");
        goto case 3; // 跳转到 case 3
    case 2:
        Console.WriteLine("这是第二个 case");
        break;
    case 3:
        Console.WriteLine("这是第三个 case");
        break;
    default:
        Console.WriteLine("这是默认的 case");
        break;
}

作用域

每一个大括号都是一个范围。
定义的变量只有在范围内生效。
大括号可以不配合流程控制语句,单独放置。

{
	int a = 12;
}
Console.WriteLine(a);//错误,无法访问到a

{
	int b = 6;
	Console.WriteLine(b);
}

{
	int b = 6;//上面的b跟这里的b互不影响,变量可以同名
	Console.WriteLine(b);
}

{
	int c = 2;
	{
		Console.WriteLine(c);//允许。因为这个代码也处于声明c的大括号范围内。
	}
}

而for循环的临时变量声明部分,作用域只限于循环内部。
所以多个并列的for循环可以都用同名的临时变量。

for (int i = 0; i < 5; i++)
{
	Console.WriteLine(i);
}
for (int i = 0; i < 5; i++)
{
	Console.WriteLine(i * i);
}

嵌入语句

如果if,else,while,do-while,for后面跟随的语句只有一条,那么可以省略大括号。

for (int i = 0; i < 10; i++)
	if (i > 5)
		Console.WriteLine(i + "大于5");
	else
		Console.WriteLine(i + "不比5大");

这是一种简写形式,有一定局限性。例如在多个if嵌套时,如果坚持省略掉大括号,就不能控制else跟随哪一个if。

int i1 = 12;
if (i1 > 4)
	if (i1 > 10)
		Console.WriteLine("通过");
	else
		Console.WriteLine("及格");

嵌入语句同样有作用域限制。所以不能是声明变量语句,因为无法使用。
但是完整的写大括号则没有这样的限制,甚至可以一条语句都没有。

int i2 = 66;
if (i2 > 50)
{
	int i3 = 60;
}
else
	int i4 = 40;//报错

流程预测

如果用作流程控制语句的判断条件完全由常量构成,那么编译器可以判断不可达代码。

if (1 < 2)
{
	Console.WriteLine("1");
}
else
{
	Console.WriteLine(2);//检测到无法访问的代码
}

while (true)//死循环
{
}
Console.WriteLine("结束");//检测到无法访问的代码

类似的,如果你声明了一个没有初始值的变量,
而它经过的流程控制语句不能保证它有初始值,那么你是无法使用他的。

int i5 = 0;
int i6;
if (i5 < 10)
{
	i6 = 10;
}
Console.WriteLine(i6);//尽管你认为一定会赋值初始值,但编译器不觉得。

如果你通过if-else,或者带有常量的条件(死循环),那么可以保证它在使用前必定赋值。

int i7 = 0;
int i8;
if (i7 < 10)
{
	i8 = 10;
}
else
{
	i8 = 20;
}
Console.WriteLine(i8);

常量

所有的字面量都是常量。此外,可以像定义变量一样定义常量。

  • 常量声明在类型前加上const
  • 常量必须写明类型,不能用var
  • 常量必须当场赋值,且之后不能再赋值。
  • 常量必须用常量赋值。所以追根溯源,所有的常量一定是通过字面量定义的。
    需要构造出来的值(包括变量)是无法为常量赋值的。
const double Pi = 3.1415926;

  1. 一些特殊情况不详细说明。
    例如int r2 = Random.Shared.Next(10);,随机数不会是负数,所以这个上限不能是负数。
    但如果这里填0,那么不会取到上限值就是无效的,他能取到0而且每次都是0。 ↩︎

你可能感兴趣的:(c#笔记-2023,c#)