C#知识学习-005(迭代语句)

目录

1.for语句

1.1 使用

1.2 具体执行步骤

1.3 迭代器

2.foreach语句

2.1 适用类型​

2.2 修改元素的值(ref用法) 

2.3 Span  

2.4 只读访问​

2.5 空集合和 null

3.do语句

4.while循环

5.break语句

6.continue语句


先来简单了解下迭代语句吧:迭代语句重复执行语句或语句块。

1.for语句

1.1 使用

  • 结构:

    for (初始化; 条件; 迭代器) { 
        循环体 
    }
  • 作用​​:在​​指定条件为真时重复执行代码​​。

  • 初始化:在循环开始前执行一次,通常用于声明并初始化循环变量(如int i = 0

  • 注意:在初始化部分声明的变量作用域仅限于循环内部

  • 条件:一个布尔表达式(如i < 3),在每次迭代前检查。如果为true,则执行循环体;否则退出循环。如果省略条件,则视为无限循环,示例:

    for ( ; ; )
    {
    
    }
  • 迭代器:在每次循环体执行后执行,通常用于更新循环变量(如i++)。

  • 举个例子吧:

for (int i = 0; i < 3; i++)
{
    Console.Write(i);
}

1.2 具体执行步骤

1.初始化:int i = 0

2.条件检查(每次迭代前执行):i < 3  ---->  0 < 3  ----> true;

3.​​循环体执行:Console.Write(i); //输出0

4.​​迭代器操作(每次循环体后执行):i++  ---->  等价于 i = i + 1  ----> i = 0 + 1;

2.条件检查(每次迭代前执行):i < 3  ---->  1 < 3  ----> true;

3.​​循环体执行:Console.Write(i); //输出1

4.​​迭代器操作(每次循环体后执行):i++  ---->  等价于 i = i + 1  ----> i = 1 + 1;

2.条件检查(每次迭代前执行):i < 3  ---->  2 < 3  ----> true;

3.​​循环体执行:Console.Write(i); //输出2

4.​​迭代器操作(每次循环体后执行):i++  ---->  等价于 i = i + 1  ----> i = 2 + 1;

2.条件检查(每次迭代前执行):i < 3  ---->  3 < 3  ----> false;循环终止

1.3 迭代器

迭代器部分可以包含零个或多个语句表达式,用逗号分隔。比较常用的表达式如下:

(1)前缀或后缀递增表达式,例如 ++i 或 i++

for (int i = 0; i < 3; ++i)  // 前缀递增
{
    Console.Write(i);
}

补充:前缀(如 ++i)和后缀(如 i++)的区别

独立语句(效果相同)

当递增操作是独立的语句时,两种写法效果完全一致:

i++;  // 相当于 i = i + 1
++i;  // 也相当于 i = i + 1

在表达式中使用(关键区别)

当它们出现在表达式中时:

  • ​前缀形式(++i)​​:先执行递增,然后返回递增后的值
  • ​后缀形式(i++)​​:先返回当前值,然后执行递增

举个例子吧:

🎯 核心区别

  • ​前缀递增​​:​​先改变变量的值,再使用它​
  • ​后缀递增​​:​​先使用变量的值,再改变它​

(2)前缀或后缀递减表达式,例如 --i 或 i--

// 递减操作
for (int i = 5; i > 0; i--)
{
    Console.Write(i);
}

(3)赋值表达式(如 x = 5

for (int i = 1; i < 8; i *= 2)  // 每次翻倍
{
    Console.Write(i);
}

(4)方法调用(如 PrintSeparator()

补充:(了解即可)

特殊用法:初始化和迭代器部分可以包含多个表达式(用逗号分隔),例如:

int i;
int j = 3;
for (i = 0, Console.WriteLine($"Start: i={i}, j={j}"); i < j; i++, j--, Console.WriteLine($"Step: i={i}, j={j}"))
{

}

2.foreach语句

  • ​​作用​​:​​遍历集合中的每个元素​​(数组、列表等)。
  • ​​语法​​:
    foreach (var item in collection)
    {
        // 处理 item
    }
  • ​​示例​​:

补充:List<int>

  • List = 列表(类似一个盒子),<int> = 盒子专门装​​整数​
  • numbers是给这个盒子起的​​名字​
  • new表示​​创建一个新的盒子​,括号 () 表示创建一个空盒子​
  • 大括号 { } 表示盒子里的内容;1, 2, 3 是放进去的三个数字
List<int> numbers = new() { 1, 2, 3 };
foreach (int num in numbers)
{
    Console.Write(num);
}

 👉 ​​相当于​​:

从 numbers 中取出第一个数字(0) → 赋值给 num→ 执行代码
再取第二个数字(1) → 赋值给 num→ 执行代码
...... 直到最后一个数字(3)

2.1 适用类型​

只要满足以下两点就能使用:

  (1)集合类型有 GetEnumerator() 方法

  (2)这个方法返回的对象包含:Current 属性 和 MoveNext() 方法

我来用通俗一点的方式解释一下:

想象你有一本书(这就是集合),foreach 想帮你一页一页地读这本书:

阅读这本书,foreach 需要三件工具:

   (1)书签,用来记住你读到哪一页了(GetEnumerator() 方法)

   (2)下一页检查器(MoveNext() 方法),检查还有下一页吗?

                返回结果:true → 还有下一页;

          false → 这是最后一页了

    (3)当前页阅读器(Current 属性),这是你的"放大镜",用来读取当前页的内容

2.2 修改元素的值(ref用法) 

在C#中,默认情况下,foreach循环中的迭代变量是集合中元素的​​副本​​。

这意味着修改迭代变量不会影响原始集合中的元素。

// 创建一个整数数组
int[] numbers = { 1, 2, 3, 4, 5 };

// 尝试在foreach循环中修改元素
foreach (int number in numbers)
{
    number = number * 2;  // 错误!因为number是只读的,不能修改
}

但是,如果集合支持直接通过引用访问元素,则可以使用ref关键字来修改原始集合中的元素。

注意:这里使用ref关键字声明迭代变量,但数组的foreach并不直接支持ref,但是Span<T> 支持。

2.3 Span<T> 

Span<T>是 C#引入的一种​​内存安全、高性能​​的数据视图类型,特别适合处理内存连续的数据块,允许你高效地操作内存而不需要复制数据。

创建 Span<T> 的常用方式是从数组创建

补充:

  • int = 整数,[] = 表示这是一个​​数组​​​
  • numbers是起的​​名字​
  • 大括号 { } 表示内容;1, 2, 3, 4, 5 是放进去的五个数字
int[] numbers = {1, 2, 3, 4, 5};

// 创建指向整个数组的 Span
Span<int> fullSpan = numbers.AsSpan();

// 创建部分数组的 Span (索引2开始,取3个元素)
Span<int> partialSpan = numbers.AsSpan(2, 3);
int[] numbers = { 1, 2, 3, 4, 5 };

// 正确示例:使用Span<T>(Span支持通过ref修改)
Span<int> numbersSpan = numbers.AsSpan(); // 将数组转换为Span

foreach (ref int number in numbersSpan)
{
    number *= 2; // 直接修改原始数组中的元素
}


foreach (var number in numbers)
{
    Console.Write(number + " "); // 输出: 2 4 6 8 10
}

2.4 只读访问

对于大型结构体,用 ref readonly 提升性能(避免复制开销)

stackalloc int[10]

  • stackalloc:在​​栈(stack)上分配内存
  • int[10]:创建包含 ​​10 个整数​​的连续内存块
  • ​相当于​​:开辟一块能存放 10 个整数的内存空间
Span<int> storage = stackalloc int[10];
int num = 0;
foreach (ref int item in storage)
{
    item = num++;//修改值
}
foreach (ref readonly var item in storage)
{
    Console.Write($"{item} "); // 结果:0 1 2 3 4 5 6 7 8 9
}

那就再多补充一点吧:

传统写法 (堆分配)

int[] array = new int[10]; // 在堆上分配

 高效写法 (栈分配)

Span<int> storage = stackalloc int[10]; // 在栈上分配
​特点​​传统 int[]stackalloc + Span
​内存位置​托管堆(Heap)栈(Stack)
​分配速度​慢(需要GC参与)极快(栈指针移动)
​释放方式​等待垃圾回收(GC)自动随栈帧弹出
​内存安全​普通安全检查有边界检查的Span
​适用场景​长期存在的大数据短生命周期的小数据

 ⚠️ 重要限制与特性:

        自动释放;大小限制​​(栈空间有限,通常约1MB);必须声明为Span;

        值类型专用​​:只能分配​​值类型​​(int, double, struct等),不能分配引用类型(如string, class)

        这种栈分配的缓存​​只适用于小型临时数据​​,大块数据仍需使用堆分配!

2.5 空集合和 null

情况结果示例
​空集合​循环体不执行new int[0]
​null 集合​抛出异常null

 空集合的例子:

int[] emptyNumbers = new int[0]; 

foreach (int num in emptyNumbers)
{
    Console.Write(num); // 永远执行不到这里
}

null集合的例子:

int[]? nullNumbers = null;

foreach (int num in nullNumbers) //尝试访问不存在的集合,会报错
{
    Console.Write(num);
}

 解释:空集合就像是猫粮袋子里面是空的,但是你可以打开;null集合就像猫粮袋子被偷走了,你又要打开猫粮袋子,所以会报错(因为你根本没有)

3.do语句

  • ​特点​​:先执行循环体,然后检查条件(条件为真则继续循环)。
  • ​确保循环体至少执行一次​​。
  • ​语法​​:
    do
    {
        // 循环体
    } while (条件);

举个例子:

int n = 0;
do
{
    Console.Write(n);
    n++;
} while (n < 5);

执行步骤​​:

  1. 初始值 n = 0
  2. 第一次循环:输出 0 → n 变为 1 → 检查条件 1 < 5 为真 → 继续
  3. 第二次循环:输出 1 → n 变为 2 → 检查条件 2 < 5 为真 → 继续
  4. 第三次循环:输出 2 → n 变为 3 → 检查条件 3 < 5 为真 → 继续
  5. 第四次循环:输出 3 → n 变为 4 → 检查条件 4 < 5 为真 → 继续
  6. 第五次循环:输出 4 → n 变为 5 → 检查条件 5 < 5 为假 → 循环结束

👉 ​​特点​​:即使一开始就不满足条件,也会执行一次 

4.while循环

  • ​特点​​:先检查条件,条件为真才执行循环体。
  • ​可能一次都不执行​​(如果初始条件为假)。
  • ​语法​​:
    while (条件)
    {
        // 循环体
    }

 举个例子:

int n = 0;
while (n < 5)
{
    Console.Write(n);
    n++;
}

 执行步骤​​:

  1. 初始值 n = 0
  2. 检查条件 0 < 5 为真 → 执行循环体:输出 0 → n 变为 1
  3. 检查条件 1 < 5 为真 → 执行循环体:输出 1 → n 变为 2
  4. 检查条件 2 < 5 为真 → 执行循环体:输出 2 → n 变为 3
  5. 检查条件 3 < 5 为真 → 执行循环体:输出 3 → n 变为 4
  6. 检查条件 4 < 5 为真 → 执行循环体:输出 4 → n 变为 5
  7. 检查条件 5 < 5 为假 → 循环结束

👉 ​​特点​​:只有条件为真时才会执行 

在迭代语句正文中的任何时间点,可以使用该break语句中断循环。 可以使用continue语句跳至循环中的下一次迭代。

5.break语句

  • 作用:立即终止当前所在的整个循环
  • 特性:完全结束循环,不再进行后续任何迭代

举例:找到32了!就停止循环,不再看后续的数字了。

int[] numbers = { 2, 8, 15, 32, 64, 128 };

foreach (int num in numbers)
{
    if (num == 32)
    {
        Console.WriteLine("over");
        break;
    }
    Console.WriteLine(num);
}

6.continue语句

  • 作用:跳过本次循环剩余的代码,直接开始下一次迭代
  • 特性:仅终止当前这次迭代,循环会继续执行

举个例子吧:

for (int i = 1; i <= 10; i++)
{
    // 如果是偶数,跳过
    if (i % 2 == 0)
    {
        Console.WriteLine("double");
        continue; // 跳到下一轮
    }

    Console.WriteLine(i);
}

现在进行一个总结:

类型执行次数特点
for0-N 次精确控制循环变量
foreach集合元素数量简化集合遍历,只读默认
do​至少 1 次​先执行后判断
while0-N 次先判断后执行

学到了这里,咱俩真棒,记得按时吃饭(周末的快乐时光总是很短暂)

【本篇结束,新的知识会不定时补充】

感谢你的阅读!如果内容有帮助,欢迎 ​​点赞❤️ + 收藏⭐ + 关注​​ 支持! 😊

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值