C#学习:分支与循环

本期我们就接着来学习C#的分支与循环

目录

if语句

[(1)单独的 if:满足条件才执行](#(1)单独的 if:满足条件才执行)

(2)if-else:二选一

[(3)if - else if - else 链:多条件判断](#(3)if - else if - else 链:多条件判断)

[(4)嵌套 if:条件内部再做判断](#(4)嵌套 if:条件内部再做判断)

switch语句

[进阶:带模式匹配的 switch(Unity 2020+ 支持)](#进阶:带模式匹配的 switch(Unity 2020+ 支持))

[更简洁的 switch 表达式(C# 8+)](# 8+))

三目运算符

类型规则:两个分支的类型必须兼容

for循环

while循环

do-while循环

foreach循环


if语句

(1)单独的 if:满足条件才执行

cs 复制代码
if (条件表达式) 
{
    // 条件为 true 时执行
}

Unity 实例:玩家按下空格键时跳跃。

cs 复制代码
if (Input.GetKeyDown(KeyCode.Space)) 
{
    rigidbody.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
}

注意条件表达式必须是 bool 类型,C# 不允许将数值直接当作布尔值(比如 if(1) 会报错)。

(2)if-else:二选一

cs 复制代码
if (health > 0) 
{
    PlayAnimation("Idle");
} 
else 
{
    PlayAnimation("Death");
}

(3)if - else if - else 链:多条件判断

适用于多个互斥条件,顺序判断,命中一个后跳出整个结构。

cs 复制代码
if (distance < 5f) 
{
    state = EnemyState.Chase;
} 
else if (distance < 20f) 
{
    state = EnemyState.Patrol;
} 
else 
{
    state = EnemyState.Idle;
}

逻辑顺序非常重要 。如果你把 distance < 20f 写在 distance < 5f 前面,那么 distance = 3f 时就会错误地进入巡逻状态,而不是追击。

(4)嵌套 if:条件内部再做判断

cs 复制代码
if (isAlive) 
{
    if (Input.GetKeyDown(KeyCode.Space)) 
    {
        Jump();
    }
}

可读性变差时,可以考虑用逻辑运算符 &&|| 合并条件,或把内层逻辑封装成独立方法。

switch语句

当你要判断一个变量的多个可能值(比如枚举的游戏状态),用 switch 比一长串 if-else if 更简洁、更易读。

cs 复制代码
enum GameState { MainMenu, Playing, Paused, GameOver }
GameState currentState;

switch (currentState) 
{
    case GameState.MainMenu:
        ShowMainMenu();
        break;
    case GameState.Playing:
        ResumeGame();
        break;
    case GameState.Paused:
        PauseGame();
        break;
    case GameState.GameOver:
        ShowGameOverScreen();
        break;
    default:
        Debug.LogWarning("未知状态");
        break;
}

关键规则

  • 每个 case 必须用 breakreturnthrowgoto 明确结束,不允许"贯穿"(fall through),除非两个 case 之间完全没有代码(可以空着直接连到下一个)。

  • default 分支是可选的,但建议写上,用来捕获意料之外的值。

  • switch 的类型支持:整数(intlongbyte等)、charstring、枚举、以及 C# 7+ 的模式匹配。

进阶:带模式匹配的 switch(Unity 2020+ 支持)

可以按类型或属性进行匹配,非常强大:

cs 复制代码
object obj = GetComponent<Collider>();
switch (obj) 
{
    case BoxCollider box:
        // 直接使用变量 box
        Debug.Log($"盒子尺寸: {box.size}");
        break;
    case SphereCollider sphere:
        Debug.Log($"球体半径: {sphere.radius}");
        break;
    case null:
        Debug.Log("没有碰撞体");
        break;
}

更简洁的 switch 表达式(C# 8+)

switch 需要返回值时,可以用更紧凑的语法:

cs 复制代码
float speed = currentState switch 
{
    GameState.Playing => 10f,
    GameState.Paused  => 0f,
    GameState.MainMenu => 0f,
    _                  => 5f  // 下划线代表默认
};

这种写法省去了 casebreak,适合简单的映射关系。

三目运算符

三目运算符(也叫条件运算符)是 C# 中唯一需要三个操作数的运算符,语法为:

bash 复制代码
条件 ? 表达式1 : 表达式2
  • 如果 条件true,计算并返回 表达式1 的值;

  • 如果 条件false,计算并返回 表达式2 的值。

比如如下这个情况

cs 复制代码
// if-else 写法
string stateText;
if (isDead)
    stateText = "阵亡";
else
    stateText = "存活";

// 三目运算符写法
string stateText = isDead ? "阵亡" : "存活";

类型规则:两个分支的类型必须兼容

三目运算符本身也有类型,它的类型由表达式1表达式2共同决定。规则如下:

  • 如果两个表达式类型相同,结果就是该类型。

  • 如果一个是 int,另一个是 float,会进行隐式提升 ,结果为 float

  • 如果一个是基类,另一个是派生类,结果为基类。

  • 如果两个类型无法隐式转换,编译器会报错。

cs 复制代码
int a = 1;
float b = 2.5f;
// 正确:int + float → 结果类型为 float
float result = condition ? a : b;   // a 会隐式转为 float

// 错误:string 和 int 之间无法转换,编译错误
// var error = condition ? "hello" : 100; // CS0173

当某分支需要 null 时,也必须确保类型明确:

cs 复制代码
// 错误:null 和 int 之间无法推断类型
// var v = cond ? 5 : null;  // 编译错误

// 正确:必须显式指定可空类型或 object
int? maybe = cond ? 5 : (int?)null;
object obj = cond ? 5 : (object)null;

for循环

在C#中,for循环与C/C++没有什么区别。当你明确知道需要执行多少次,或者需要一个索引变量时,for 是首选。

cs 复制代码
for (初始化; 条件; 迭代) 
{
    // 循环体
}

遍历数组/列表的经典用法

cs 复制代码
GameObject[] enemies = GameObject.FindGameObjectsWithTag("Enemy");
for (int i = 0; i < enemies.Length; i++) 
{
    enemies[i].GetComponent<Enemy>().TakeDamage(10);
}

倒序循环(从末尾开始,常用于移除元素时避免索引错乱):

cs 复制代码
for (int i = list.Count - 1; i >= 0; i--) 
{
    if (list[i].IsDead) 
    {
        list.RemoveAt(i); // 倒序移除不会影响未处理元素的索引
    }
}

注意 :不要在 for 的循环体内增删正被遍历的集合,否则会导致异常或跳过元素,倒序是解决方案之一。

while循环

当你事先不知道循环次数,只在满足某个条件时一直循环,用 while

cs 复制代码
while (条件) 
{
    // 循环体
}

Unity 协程中的动画等待

cs 复制代码
IEnumerator FadeOut(CanvasGroup group, float duration) 
{
    float elapsed = 0f;
    while (elapsed < duration) 
    {
        elapsed += Time.deltaTime;
        group.alpha = Mathf.Lerp(1f, 0f, elapsed / duration);
        yield return null; // 每帧循环一次
    }
}

注意 :务必确保循环条件最终会变为 false,否则死循环会让 Unity 编辑器直接卡死。你只能通过任务管理器强退。

do-while循环

循环体先执行一遍,然后再检查条件。用在"无论条件如何,至少要先做一次"的场景,比如游戏里"先扣血,再检查是否死亡"。

cs 复制代码
int hp = 100;
do 
{
    hp -= 15;
    Debug.Log("受到15点伤害,剩余:" + hp);
} while (hp > 0);
// 即使初始hp就≤0,也会先执行一次扣血再判断

在 Unity 游戏逻辑中用得相对少一些,但在输入验证、流程确保一次执行时有它的价值。

foreach循环

foreach 能极为简洁地遍历集合或数组。

cs 复制代码
List<Enemy> enemies = GetAllEnemies();
foreach (Enemy enemy in enemies) 
{
    enemy.TakeDamage(5);
}

优点 :没有索引变量,代码干净,不会越界。
Unity 中的重大陷阱 :在 Unity 中,普通的 foreach 遍历 ListDictionary 等会在堆上分配一个小的枚举器对象 ,如果放在每帧执行的 Update 里,会产生大量垃圾内存,导致 GC(垃圾回收)造成卡顿

规避方案

  • List 使用 for 代替:这是最简单、最高效的方法。

  • Array 使用 foreach 是安全的 :C# 编译器对数组的 foreach 做了优化,不会产生堆分配。

  • 使用非分配型的集合 :如 C# 新增的 Span<T> 或第三方库的 FastList

  • 如果必须用 foreach 且不是数组 ,尽量避免在 UpdateFixedUpdate 等高频方法中使用。

本期内容到这里就结束了,喜欢请点个赞谢谢

封面图自取:

相关推荐
用户713874229009 小时前
OAuth 2.0 中的state参数:从规范到实践的深度解析
后端
倚栏听风雨9 小时前
StateGraph 详细分析
后端
用户713874229009 小时前
Cookie 深度技术指南:从原理到安全实践
后端
正运动技术9 小时前
C#运动控制开源(二): CAD导图和小线段速度前瞻优化
c#·正运动技术·运动控制器·运动控制卡·正运动控制器·运动控制开源·ethercat运动控制器
倚栏听风雨9 小时前
AsyncCommandAction 详细分析
后端
倚栏听风雨9 小时前
Edge 详细分析
后端
倚栏听风雨9 小时前
CompiledGraph 详细分析
后端
装不满的克莱因瓶9 小时前
【项目亮点四】支付订单超时处理与状态补偿机制设计
java·开发语言·后端·rabbitmq·消息中间件
XovH9 小时前
Django 从 0 到 1 打造完整电商平台:商品列表页实现
后端