-
循环语句:for、while与foreach
2.4 循环语句:for、while与foreach
在程序开发中,经常需要重复执行某段代码(如计算1到100的和、遍历数组元素、验证用户输入等)。循环语句是实现这一需求的核心工具,C#提供三种常用循环:for循环(已知循环次数)、while循环(未知循环次数,条件控制)和foreach循环(遍历集合/数组)。本节将通过实例详解每种循环的语法、执行逻辑、适用场景,并延伸讲解循环控制、嵌套循环等进阶知识。
2.4.1 for循环:精确控制循环次数
概念与语法
for循环适用于已知循环次数的场景,通过“初始化变量→条vb.net教程C#教程python教程SQL教程access 2010教程件判断→执行循环体→更新变量”四步完成一次迭代,语法结构清晰,控制力强。
基本语法:
csharp
for (初始化表达式; 条件表达式; 迭代表达式)
{
// 循环体:条件为true时执行的代码块
}
初始化表达式:循环开始前执行一次,通常用于声明和初始化循环变量(如int i = 0)。
条件表达式:每次循环前判断,结果为true则执行循环体,false则退出循环(如i < 10)。
迭代表达式:每次循环体执行后执行,用于更新循环变量(如i++,即i = i + 1)。
实例2-7:用for循环计算1到10的累加和
需求:计算1+2+3+...+10的结果并输出,通过for循环清晰控制从1到10的迭代过程。
csharp
using System;
class ForLoopDemo
{
static void Main()
{
// 1. 声明变量存储累加结果
int sum = 0; // 初始值0,后续每次循环累加i的值
// 2. for循环:从1迭代到10,每次累加i到sum
for (int i = 1; // 初始化:循环变量i=1(仅执行一次)
i <= 10; // 条件:i<=10?true则执行循环体,false则退出
i++ // 迭代:循环体执行后,i自增1(i = i + 1)
)
{
sum = sum + i; // 循环体:累加i到sum(等价于sum += i)
Console.WriteLine($"当前i={i},累加和sum={sum}"); // 打印中间过程(可选)
}
// 3. 输出最终结果
Console.WriteLine($"1到10的累加和:{sum}"); // 输出:55
}
}
逐行代码讲解:
using System;:引入System命名空间,包含Console类用于输入输出。
class ForLoopDemo:定义类,C#代码需放在类中,类名遵循Pascal命名法。
static void Main():程序入口方法,循环代码在此执行。
int sum = 0;:声明sum变量存储累加结果,初始值0(若不初始化,局部变量会编译报错)。
for (int i = 1; i <= 10; i++):
初始化:int i = 1声明循环变量i并赋值1,仅在循环开始时执行一次。
条件:i <= 10,每次循环前判断:第1次i=1→true,执行循环体;第10次i=10→true,执行;第11次i=11→false,退出循环。
迭代:i++,循环体执行后,i自增1(i从1→2→...→11)。
sum = sum + i;:循环体核心逻辑,每次将i的值累加到sum。例如:
第1次循环:sum=0+1=1
第2次循环:sum=1+2=3
...
第10次循环:sum=45+10=55
Console.WriteLine($"当前i={i},累加和sum={sum}");:打印每次迭代的中间结果,帮助理解循环执行过程(实际开发中可省略)。
最终输出1到10的累加和:55,符合预期(1+2+...+10=55)。
2.4.2 while循环:条件驱动的循环
概念与语法
while循环适用于循环次数不确定的场景,仅通过“条件表达式”控制循环是否继续执行。分为两种形式:
while循环:先判断条件,条件为true才执行循环体(可能一次不执行)。
do-while循环:先执行一次循环体,再判断条件(至少执行一次)。
基本语法:
csharp
// while循环(先判断后执行)
while (条件表达式)
{
// 循环体
}
// do-while循环(先执行后判断)
do
{
// 循环体
} while (条件表达式); // 注意末尾分号
实例2-8:while循环实现用户输入验证
需求:提示用户输入1-100之间的整数,若输入无效(非数字或超出范围),则重复提示,直到输入有效数字为止(循环次数不确定)。
csharp
using System;
class WhileLoopDemo
{
static void Main()
{
int inputNum; // 存储用户输入的有效数字
bool isValid = false; // 标记输入是否有效(初始无效)
// while循环:条件isValid == false(输入无效时继续循环)
while (!isValid) // !isValid等价于isValid == false
{
Console.Write("请输入1-100之间的整数:");
string input = Console.ReadLine(); // 读取用户输入的字符串
// 尝试将字符串转换为int(使用TryParse避免异常)
if (int.TryParse(input, out inputNum)) // TryParse成功则返回true,inputNum为转换后的值
{
// 判断数字是否在1-100范围内
if (inputNum >= 1 && inputNum <= 100)
{
isValid = true; // 输入有效,标记为true,下次循环条件为false,退出循环
Console.WriteLine($"输入有效!您输入的数字是:{inputNum}");
}
else
{
Console.WriteLine("输入超出范围!请重新输入。"); // 范围无效,继续循环
}
}
else
{
Console.WriteLine("输入格式错误!请输入整数。"); // 格式无效,继续循环
}
}
}
}
逐行代码讲解:
int inputNum;:声明变量存储最终有效的整数(作用域在循环外,方便后续使用)。
bool isValid = false;:isValid为“输入是否有效”的标记,初始false(假设输入无效)。
while (!isValid):循环条件为“输入无效”(isValid == false),只要输入无效就持续循环。
Console.Write("请输入1-100之间的整数:");:提示用户输入,Write不换行。
string input = Console.ReadLine();:读取用户输入的原始字符串(可能是“50”“abc”“150”等)。
int.TryParse(input, out inputNum):安全转换字符串到int:
若输入是有效整数(如“50”),TryParse返回true,inputNum被赋值50;
若输入无效(如“abc”),返回false,inputNum为int默认值0(但此时不关心其值)。
内层if (inputNum >= 1 && inputNum <= 100):判断转换后的数字是否在1-100范围内:
若有效(如50),isValid = true,循环条件变为false,退出循环,输出结果;
若无效(如150),提示“超出范围”,isValid仍为false,继续循环。
外层else:TryParse失败(如输入“abc”),提示“格式错误”,继续循环。
执行流程示例(用户输入“abc”→“150”→“50”):
1.第1次循环:输入“abc”→TryParse失败→提示“格式错误”→isValid仍false→继续循环。
2.第2次循环:输入“150”→TryParse成功→inputNum=150→范围判断失败→提示“超出范围”→继续循环。
3.第3次循环:输入“50”→TryParse成功→范围判断成功→isValid=true→输出“输入有效”→循环条件!isValid为false→退出循环。
实例2-8扩展:do-while循环(至少执行一次)
若希望“无论条件如何,循环体至少执行一次”(如强制用户输入一次),可使用do-while循环:
csharp
// do-while循环:先执行一次循环体,再判断条件
do
{
Console.Write("请输入1-100之间的整数:");
string input = Console.ReadLine();
if (int.TryParse(input, out inputNum) && inputNum >= 1 && inputNum <= 100)
{
isValid = true;
Console.WriteLine($"输入有效!您输入的数字是:{inputNum}");
}
else
{
Console.WriteLine("输入无效,请重新输入!");
}
} while (!isValid); // 循环条件:输入无效则继续
区别:while循环可能一次不执行(初始条件false),do-while循环至少执行一次(先执行后判断)。
2.4.3 foreach循环:遍历集合的“语法糖”
概念与语法
foreach循环用于遍历集合或数组(如int[]、List
基本语法:
csharp
foreach (元素类型 元素变量 in 集合/数组)
{
// 循环体:使用元素变量访问当前元素
}
集合/数组:必须是实现IEnumerable接口的类型(如数组、List、字典等)。
元素变量:每次迭代时,自动获取集合中的下一个元素,只读(不能修改其值,除非是引用类型修改属性)。
实例2-9:用foreach遍历数组并计算平均值
需求:定义一个存储学生成绩的数组,用foreach循环遍历所有成绩,计算总和与平均值。
csharp
using System;
class ForeachLoopDemo
{
static void Main()
{
// 1. 定义学生成绩数组(引用类型,存储在堆中,数组名是引用地址)
int[] scores = { 90, 85, 78, 92, 88 }; // 5个学生的成绩
// 2. 声明变量存储总和与数量
int total = 0;
int count = 0;
// 3. foreach循环遍历数组元素
foreach (int score in scores) // score依次取数组中的每个元素:90→85→78→92→88
{
total += score; // 累加当前成绩到总和(total = total + score)
count++; // 统计学生数量(count从0→1→2→3→4→5)
Console.WriteLine($"当前成绩:{score},累计总和:{total}");
}
// 4. 计算并输出平均值
double average = (double)total / count; // 强制转换为double,避免整数除法(如433/5=86.6而非86)
Console.WriteLine($"学生成绩平均值:{average:F1}"); // 输出:86.6(保留1位小数)
}
}
逐行代码讲解:
int[] scores = { 90, 85, 78, 92, 88 };:声明int数组scores,初始化5个元素(数组是引用类型,在堆中存储元素,scores是栈中的引用地址)。
int total = 0; int count = 0;:total累加成绩总和,count统计元素数量(也可直接用scores.Length获取数组长度,此处为演示遍历过程)。
foreach (int score in scores):遍历数组scores,每次迭代将当前元素赋值给score:
第1次:score=90→total=90,count=1
第2次:score=85→total=175,count=2
...
第5次:score=88→total=433,count=5
double average = (double)total / count;:计算平均值,需将total强制转换为double(否则433/5会做整数除法,结果为86),转换后结果为433.0/5=86.6。
Console.WriteLine($"学生成绩平均值:{average:F1}");:{average:F1}保留1位小数,输出“86.6”。
foreach的局限性:
不能修改元素值:若尝试score = 100,编译报错(score是只读变量);
不能跳过或修改集合:不能使用break外的方式提前退出(除非用break),不能增删数组元素。
2.4.4 知识扩展:循环控制与对比分析
- 循环控制语句:break与continue
break:立即退出整个循环,执行循环后的代码(无论后续条件是否满足)。
实例:用for循环查找数组中的第一个偶数,找到后退出循环:
csharp
int[] nums = { 3, 5, 8, 9, 12 };
for (int i = 0; i < nums.Length; i++)
{
if (nums[i] % 2 == 0) // 判断是否为偶数
{
Console.WriteLine($"找到第一个偶数:{nums[i]}"); // 输出:8
break; // 退出循环,后续元素不再判断
}
}
continue:跳过当前迭代的剩余代码,直接进入下一次迭代(不退出循环)。
实例:用while循环累加1到10中的奇数(跳过偶数):
csharp
int sum = 0;
int i = 1;
while (i <= 10)
{
if (i % 2 == 0) // 若为偶数,跳过当前迭代
{
i++; // 必须手动更新循环变量,否则死循环
continue;
}
sum += i; // 仅累加奇数:1+3+5+7+9=25
i++;
}
Console.WriteLine($"1-10的奇数和:{sum}"); // 输出:25
-
三种循环的适用场景对比
循环类型 核心特点 适用场景 优势 局限性
for 初始化、条件、迭代分离,精确控制次数 已知循环次数(如遍历前100个数) 变量作用域可控(循环内定义i),效率高 语法较繁琐,需手动管理索引
while 仅条件控制,循环次数不确定 依赖外部条件结束(如用户输入0停止) 灵活,适合动态条件 需在循环外定义变量,易忘记更新迭代变量导致死循环
foreach 遍历集合,无需索引 访问数组、List等集合的每个元素 语法简洁,避免索引越界错误 不能修改元素值,不能控制迭代顺序 -
死循环的原因与避免
死循环:循环条件永远为true,导致程序无法退出(如while (true) { ... }未加break)。常见原因:
条件表达式恒为true(如while (1 == 1));
迭代变量未更新(如for (int i=0; i<10; ) { ... }忘记i++);
条件依赖外部变量,但变量未被正确修改(如while (input != 0)中input始终不为0)。
避免方法:
循环中确保条件会在有限次迭代后变为false;
关键循环(如网络请求、文件读取)添加“最大重试次数”限制(如for (int retry=0; retry < 3; retry++));
调试时用Console.WriteLine打印循环变量,观察是否更新。
2.4.5 基础知识延伸:嵌套循环与性能优化 -
嵌套循环:循环内包含循环
嵌套循环用于处理多维数据(如矩阵、表格),外层循环控制“行”,内层循环控制“列”。
实例:打印九九乘法表
csharp
for (int i = 1; i <= 9; i++) // 外层循环:控制乘数(1-9)
{
for (int j = 1; j <= i; j++) // 内层循环:控制被乘数(1-i),避免重复(如2×3和3×2)
{
Console.Write($"{j}×{i}={i*j} "); // 是制表符,对齐输出
}
Console.WriteLine(); // 内层循环结束后换行
}
执行逻辑:外层i=1时,内层j=1→输出“1×1=1”;i=2时,j=1→“1×2=2”,j=2→“2×2=4”;...;i=9时,j=1到9,输出9个算式。
2. 循环性能优化技巧
减少循环内的计算:将循环外不变的计算(如array.Length)提取到循环外,避免重复计算:
csharp
// 优化前(每次循环都计算nums.Length)
for (int i=0; i < nums.Length; i++) { ... }
// 优化后(仅计算一次)
int len = nums.Length;
for (int i=0; i < len; i++) { ... }
优先使用foreach遍历集合:foreach由编译器优化,遍历数组时性能接近for循环,且代码更安全(无索引越界风险)。
避免嵌套循环过深:超过3层嵌套会降低可读性,可拆分为方法或使用LINQ查询替代。
总结
for循环:通过初始化、条件、迭代三部分控制,适合已知循环次数的场景(如1到n的计算)。
while/do-while循环:依赖条件表达式,适合循环次数不确定的场景(如用户输入验证),do-while确保至少执行一次。
foreach循环:简化集合遍历,无需索引,适合数组、List等集合的读取操作。
循环控制:break退出整个循环,continue跳过当前迭代;需避免死循环,确保条件会更新。
进阶应用:嵌套循环处理多维数据(如九九乘法表),性能优化需减少循环内计算、避免过深嵌套。
掌握循环语句是实现重复逻辑的基础,后续学习数组、集合、LINQ等知识时,循环的灵活运用将大幅提升代码效率。
来源:https://www.xin3721.com/ArticlecSharp/c49347.html










