4、C#编程基础(中):条件判断、循环、数组与List

好,咱们接着聊。上一章我们把变量、数据类型和运算符过了一遍,算是把“砖块”备齐了。这一章,咱们要开始学怎么用这些砖块搭出真正的结构——也就是控制程序的流程。

说白了,程序就是一条条指令按顺序执行。但光有顺序不够,你得让程序会“思考”:如果这样,就做A;如果那样,就做B。还得让程序会“重复”:把同一件事做100遍、1000遍。这就是条件判断和循环要做的事。

我个人习惯,学编程最先要搞懂的就是这两个东西。它们就像游戏里的“逻辑开关”和“自动循环脚本”,搞懂了,你就能写出真正有用的代码了。

4.1 条件判断:让程序学会“做选择”

先说说条件判断。你想想看,游戏里玩家按了跳跃键,角色就跳起来;没按,就不跳。这就是一个典型的“如果……就……”的逻辑。

4.1.1 if / else if / else

这是最基础、最常用的条件判断结构。写法很直白:

int hp = 50;

if (hp > 0)
{
    Console.WriteLine("角色还活着,继续战斗!");
}
else
{
    Console.WriteLine("角色已阵亡,游戏结束。");
}

这里有个细节要注意:if 后面跟的是布尔表达式,结果只能是 true 或 false。如果条件不止两个,就用 else if 来串联:

int score = 85;

if (score >= 90)
{
    Console.WriteLine("评级:S");
}
else if (score >= 80)
{
    Console.WriteLine("评级:A");
}
else if (score >= 70)
{
    Console.WriteLine("评级:B");
}
else
{
    Console.WriteLine("评级:C,需要加油了");
}
小技巧:判断顺序很重要。把最可能满足的条件写在前面,能少做几次判断,代码跑起来更快。我在项目中就见过有人把范围写反了,结果永远进不了正确的分支。

4.1.2 switch 语句

当你要判断的值是固定的几个选项时,用 switchif/else 更清爽。比如处理玩家输入的命令:

string command = "攻击";

switch (command)
{
    case "攻击":
        Console.WriteLine("执行攻击动画,造成伤害");
        break;
    case "防御":
        Console.WriteLine("进入防御姿态,减伤50%");
        break;
    case "使用道具":
        Console.WriteLine("打开背包界面");
        break;
    default:
        Console.WriteLine("未知指令,角色原地待命");
        break;
}

嗯,这里要注意:每个 case 后面一定要加 break,不然会“穿透”到下一个 case 去执行。我曾经在项目里调试一个奇怪的bug,找了半天才发现是少写了一个 break,结果玩家按“防御”键,角色一边防御一边还使用了道具……

避坑指南:switch 的 case 值必须是常量,不能是变量。而且 C# 的 switch 不支持像某些语言那样的“范围判断”,比如你不能写 case > 10:。这种情况老老实实用 if/else。

4.2 循环:让程序学会“重复劳动”

写游戏的时候,经常要处理一堆东西:比如遍历所有敌人、刷新所有子弹的位置、播放一连串的音效。这时候循环就派上用场了。

4.2.1 for 循环

for 循环适合“我知道要循环多少次”的场景。比如让一个敌人连续攻击5次:

for (int i = 0; i < 5; i++)
{
    Console.WriteLine($"第 {i + 1} 次攻击!");
}

这里 i 是循环变量,从0开始,每次加1,直到 i < 5 不成立为止。注意循环次数是5次,但 i 的值是0到4。很多新手会在这里犯晕,写成 i <= 5,结果多循环了一次。

核心要点:for 循环的三个部分——初始化、条件判断、迭代更新——缺一不可。但你可以把某些部分写在别处,比如 int i = 0; 可以提前声明。不过我个人建议,除非有特殊需要,否则都写在一起,代码更清晰。

4.2.2 while 循环

while 循环适合“我不知道要循环多少次,但我知道什么时候停下来”。比如等待玩家输入正确的密码:

string input = "";
while (input != "1234")
{
    Console.Write("请输入密码:");
    input = Console.ReadLine();
}
Console.WriteLine("密码正确,门已打开!");

还有一种 do...while 循环,它保证至少执行一次循环体:

int attempts = 0;
do
{
    Console.WriteLine($"第 {++attempts} 次尝试...");
    // 模拟尝试连接服务器
} while (attempts < 3);

为什么会有 do...while?你想想看,有些操作你无论如何都得先做一次,比如先显示一次提示信息,再判断要不要继续。这时候 do...while 就比 while 更合适。

4.2.3 foreach 循环

这个我特别喜欢用。它专门用来遍历集合里的每一个元素,不用管索引,不用管长度,直接拿数据:

string[] enemies = { "史莱姆", "哥布林", "骷髅兵", "Boss" };

foreach (string enemy in enemies)
{
    Console.WriteLine($"正在攻击:{enemy}");
}

你看,多简洁。不需要 i,不需要 enemies[i],直接就是每个元素本身。我在项目中处理列表数据时,90%的情况都用 foreach,只有需要修改元素或知道索引位置时才用 for

注意:foreach 循环里不能修改正在遍历的集合(比如添加或删除元素),否则会抛出异常。如果你需要边遍历边修改,老老实实用 for 循环倒着遍历。

4.3 数组与 List:批量管理数据

单个变量只能存一个值。但游戏里哪有这么简单?一个背包里有几十件道具,一个关卡里有上百个敌人。这时候就需要“容器”来装它们。

4.3.1 数组

数组是最基本的容器。它的大小固定,创建后不能改变。适合存储数量已知且不变的数据:

// 声明并初始化一个长度为3的数组
int[] playerLevels = new int[3];
playerLevels[0] = 10;
playerLevels[1] = 15;
playerLevels[2] = 20;

// 或者直接初始化
string[] weaponNames = { "木剑", "铁剑", "钢剑", "魔剑" };

// 访问元素
Console.WriteLine(weaponNames[0]); // 输出:木剑

数组的索引从0开始,这点一定要刻在脑子里。我见过太多新手写 weaponNames[4] 结果数组越界了——因为数组只有4个元素,索引最大是3。

数组操作 代码示例 说明
获取长度 weaponNames.Length 返回数组元素个数
遍历 for (int i = 0; i < arr.Length; i++) 配合索引访问
排序 Array.Sort(arr) 对数组元素排序

4.3.2 List:更灵活的数组

数组好用,但太死板了。你想想,玩家的背包随时可能捡到新道具,难道每次都要重新创建一个更大的数组?这时候 List<T> 就登场了。

List 是 C# 里最常用的集合类型。它的大小可以动态变化,想加就加,想删就删:

// 需要先引入命名空间
// using System.Collections.Generic;

List<string> inventory = new List<string>();

// 添加道具
inventory.Add("红药水");
inventory.Add("蓝药水");
inventory.Add("回城卷轴");

// 插入到指定位置
inventory.Insert(1, "解毒草");

// 删除道具
inventory.Remove("蓝药水");     // 按值删除
inventory.RemoveAt(0);          // 按索引删除

// 获取数量
Console.WriteLine($"背包里有 {inventory.Count} 件道具");

// 遍历
foreach (string item in inventory)
{
    Console.WriteLine($"道具:{item}");
}
为什么用 List 而不是数组?说白了,90%的情况下 List 都比数组好用。数组唯一的优势是性能略高,但除非你在做性能极其敏感的系统(比如每帧处理数万个对象),否则 List 完全够用。我个人在项目里几乎只用 List,数组只在定义固定常量时用一下。

4.4 综合小案例:简单的战斗系统

光说不练假把式。咱们把今天学的知识串起来,写一个简单的回合制战斗逻辑:

// 玩家和敌人的血量
int playerHp = 100;
int enemyHp = 80;
int round = 1;

// 玩家的技能列表
List<string> skills = new List<string>() { "普通攻击", "火球术", "治疗术" };

Console.WriteLine("战斗开始!");

while (playerHp > 0 && enemyHp > 0)
{
    Console.WriteLine($"\n=== 第 {round} 回合 ===");
    Console.WriteLine($"玩家血量:{playerHp},敌人血量:{enemyHp}");

    // 玩家选择技能
    Console.WriteLine("请选择技能:");
    for (int i = 0; i < skills.Count; i++)
    {
        Console.WriteLine($"{i + 1}. {skills[i]}");
    }

    string input = Console.ReadLine();
    int choice;
    if (int.TryParse(input, out choice) && choice >= 1 && choice <= skills.Count)
    {
        string selectedSkill = skills[choice - 1];
        Console.WriteLine($"玩家使用了 {selectedSkill}!");

        // 根据技能不同,效果不同
        if (selectedSkill == "普通攻击")
        {
            enemyHp -= 15;
        }
        else if (selectedSkill == "火球术")
        {
            enemyHp -= 30;
        }
        else if (selectedSkill == "治疗术")
        {
            playerHp += 20;
        }
    }
    else
    {
        Console.WriteLine("无效输入,玩家发呆了一回合...");
    }

    // 敌人反击(如果还活着)
    if (enemyHp > 0)
    {
        playerHp -= 10;
        Console.WriteLine("敌人攻击了玩家,造成10点伤害!");
    }

    round++;
}

// 战斗结果
if (playerHp > 0)
{
    Console.WriteLine("\n🎉 玩家胜利!");
}
else
{
    Console.WriteLine("\n💀 玩家被击败了...");
}

这个例子虽然简单,但把 if/else、for、while、List 都用上了。你可以在自己的电脑上跑一跑,改改数值,加加技能,感受一下控制流程是怎么运作的。

课后建议:试着给这个战斗系统加一个“暴击”机制——当随机数大于某个值时,伤害翻倍。用 Random 类就能实现。这样你就能把条件判断和循环玩得更溜了。

好了,这一章的内容就到这儿。条件判断和循环是编程的“骨架”,数组和 List 是“容器”。骨架搭好了,容器备齐了,下一章咱们就可以开始写更复杂的逻辑了。记住,多写、多试、多犯错——犯错才是学习最快的方式。