[C.C++] C# 初步入门(认识、变量、运算符、控制流程)

68 0
Honkers 前天 09:25 来自手机 | 显示全部楼层 |阅读模式

本文用于C#学习引导入门,运用的编译器是visual studio2022,主要涉及认识C#、C#的变量、C#的运算符、C#的流程控制;通过文字解析和相关代码示例进行讲解,简洁明了初步理解C#,对C#有个初步认识,可以做到上手写代码。

一、对C#的认识

官方来讲:C#(读作 C Sharp)是微软推出的面向对象、类型安全、现代化编程语言,专门搭配 .NET 平台使用。他是一门编程语言,与C语言、C++、JAVA类似,主要用于程序员与计算机通过编译器等进行交流,类似人类与计算机进行交流的翻译官。C#的主要用途有:

  • 控制台程序 简单工具、脚本、后台处理程序。
  • Windows 桌面软件 WinForms、WPF,电脑桌面客户端(管理系统、工具软件)。
  • 网站 & Web 后端 ASP.NET Core,企业后台接口、网站、管理平台,跨 Windows/Linux 部署。
  • 游戏开发(最热门用途) 搭配 Unity 引擎,90% 手游、独立游戏、主机游戏都用 C#。
  • 移动端 App MAUI / Xamarin,一套代码同时开发安卓、苹果手机软件。
  • 物联网、嵌入式、桌面原生程序 .NET Native AOT 可编译成无依赖独立程序,适合硬件设备。
  • 微服务、云服务、异步高并发项目 自带 Task 异步、完善线程与内存管理,企业后端主流选型。

这些内容大家简单看看就好,对于面向对象、类型安全、现代化编程语言的理解后续学习会进行探讨,现在在这里进行一个简单的赘述:

面向对象→一切皆对象:基础类型 int、string 本质也是类;支持类、继承、多态、接口、抽象类、封装。

类型安全→变量必须指定类型,编译阶段就检查类型错误,不会运行时突然崩溃; 不允许随便强制转换无关类型,减少内存越界、非法访问。

现代化编程语言→语法简洁干练、功能强大等。

二、认识第一个C#程序

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. namespace Task01
  7. {
  8. internal class Program
  9. {
  10. static void Main(string[] args)
  11. {
  12. Console.WriteLine("Hello C#");
  13. }
  14. }
  15. }
复制代码

除了Console.WriteLine("Hello C#");是我们自己写的,其余内容都是编译器自动生成的,namespace Task01的Task01是我们在创建项目的项目名称;大家在这里只需要认识到static void Main(string[] args) 是C# 控制台唯一入口,程序启动自动执行;其余代码后续学习理解即可,Console.WriteLine("Hello C#")的作用是打印Hello C#,运行效果如下:

三、变量与数据类型

以在校大学生信息存储场景为例,学生包含学号、姓名、性别、血型、年龄等多项个人信息。若要让计算机保存、记录这类信息,就需要借助变量,由此可得:变量是编程语言中用于承载各类数据信息的载体。

学生各项信息具备明显差异化特征:如学号为数字串 2390004362、姓名为文本 “小帅”、年龄为整数 18、性别为文字 “男”、血型为字符 “O”。各类数据在组成形式、字符长度、取值规则上均存在区别,因此需要划分不同的数据类型进行区分存储。计算机依靠变量、对应数据类型以及赋予变量的变量值(如年龄对应的数值 18),完成对各类业务数据的存储与管理。

3.1变量

3.1.1变量的三要素

变量需要有变量名、数据类型、变量值,例如小帅的年龄18岁表示为:int age=18;int是数据类型,age是变量名(我们自己取的),18是变量值。

3.1.2变量的命名规范

(一)硬性强制规则

(1)只能由字母、数字、下划线_组成  不能出现空格、中文、特殊符号(#¥%&@之类),比如学生姓名、stu-name、stu#age全是违规,电脑直接不认。

(2)不能以数字开头  18age不行,纯数字放开头程序直接罢工,换成age18、stu_age才合规。

(3)严格区分大小写  Name和name是两个完全不搭边的变量,别手滑大小写混用,找 bug 能找到秃头。

(4)禁止使用编程语言关键字 像int、if、for、class这类系统自带词不能当变量名,相当于抢系统饭碗,程序直接炸。

(二)行业通用可读性规范

(1)见名知意,拒绝玄学命名 千万别用a、b、x这种抽象代号,过两天自己都忘了存的啥。存学生年龄就写stu_age,学号就写stu_id,一目了然。

(2)主流两种命名风格

  • 下划线命名:单词之间用下划线隔开,stu_blood_type、stu_sex
  • 小驼峰命名:首个单词小写,后面单词首字母大写,stuBloodType、stuName​​​​​​

(3)不要过长、不要堆砌单词 不用写一长串废话the_age_of_student_in_class_one,简洁stu_age足够,太长看着费眼。

(4)常量全大写,普通变量小写 固定不变的数据(比如学校代码)写成大写SCHOOL_CODE,普通存储信息的变量统一小写,一眼区分常量和变量。

(5)尽量使用英文,少用拼音 尽量用标准英文单词,别整xingming、nianling这种拼音,写大型项目会显得很业余;实在词汇匮乏再简短拼音凑合。

3.2变量的数据类型

C#的数据类型分为两类:值类型和引用类型

3.1.1值类型

值类型主要包括:整数类型、浮点类型、字符类型、布尔类型。

(1)整数类型:主要是存贮整数,例如年龄18表示为:int age=18;int 表示为整数类型。整形类型的分类如下:

数据类型占用字节取值范围是否常用
sbyte1 字节-128 ~ 127极少用,底层硬件开发
byte1 字节0 ~ 255常用,存储字节、颜色、状态码
short2 字节-32768 ~ 32767较少用,节省空间的小整数
ushort2 字节0 ~ 65535极少用
int4 字节-2147483648 ~ 2147483647最常用,普通数字、学号、年龄等
uint4 字节0 ~ 4294967295偶尔使用,仅存非负大数
long8 字节-9223372036854775808 ~ 9223372036854775807常用,超大数值、订单号、时间戳
ulong8 字节0 ~ 18446744073709551615很少用

(2)浮点类型:主要是存储小数,例如:微信余额float balance=100.99f;float表示浮点数类型。浮点数类型分类如下:

数据类型占用字节有效精度取值范围是否常用
float4 字节7 位有效数字±1.5×10⁻⁴⁵ ~ ±3.4×10³⁸偶尔使用,图形、游戏、性能优先场景
double8 字节15~16 位有效数字±5.0×10⁻³²⁴ ~ ±1.7×10³⁰⁸最常用,普通小数、计算、价格、坐标默认使用
decimal16 字节28~29 位有效数字±1.0×10⁻²⁸ ~ ±7.9×10²⁸常用,金融、金额、财务高精度计算

(3)字符类型:主要存储字符,例如:人的血型char blood_type='A';char表示浮点数类型,浮点数类型分类如下:

数据类型占用字节取值范围代码示例
char2 字节0 ~ 65535(Unicode U+0000 ~ U+FFFF)char gender = '男';char bloodType = 'O';

(4)布尔类型:主要存储一个结果的正误,例如:判断结果bool result=ture;bool表示布尔类型,布尔类型的结果只有ture和flase;

数据类型占用字节取值范围代码示例
bool1 字节仅两个值:true(真)、false(假)bool isStudent = true;bool isMale = false;

3.1.2引用类型

值类型(int、char、bool、decimal 等):数据直接存放在栈内存,变量里装的就是真实数据本身; 引用类型:真实数据存放在堆内存,栈中变量只存放堆内存的地址(引用地址),通过地址去堆里找到真实内容。

C# 主要引用类型分类:string 字符串类型、object 对象类型(所有类型的基类)、自定义类(class)、接口 interface、数组 Array、委托 delegate、事件 event、装箱后的值类型;我们这里简单讲解一下string类型和object类型;

(一)string类型

(1)基础特性:属于引用类型,但行为有特殊性;底层存储一串 Unicode 字符,使用双引号包裹;不可变(只读):一旦赋值无法修改,拼接、替换字符串时会在堆中新建字符串,旧字符串等待 GC 回收。

(2)占用和使用

  1. // 直接赋值,无需new
  2. string name = "小帅";
  3. string id = "2390004362";
  4. // 字符串拼接,会生成新字符串
  5. string info = name + "学号:" + id;
复制代码

(二)object类型

(1)定位:C# 所有类型的根父类

不管是值类型(int、bool)还是引用类型(string、自定义类),全部隐式继承 object,是所有数据的统一顶层类型。

(2)装箱与拆箱(核心用法)

  • 装箱:值类型 → object(栈数据拷贝到堆,返回引用地址)

  • 拆箱:object → 值类型(强制转换取出堆内原始数据)

  1. // 装箱
  2. int age = 18;
  3. object obj = age;
  4. // 拆箱
  5. int newAge = (int)obj;
复制代码

3.2var 隐式类型推断

3.2.1var 是什么

var 是隐式类型推断关键字,编译器会根据右侧赋值的内容,自动推断出变量真实数据类型,不用手动写完整类型名。 本质:变量依然是强类型,类型在编译阶段就确定,不是动态类型。

  1. // 显式写法
  2. int age = 18;
  3. string name = "小帅";
  4. // var隐式推断
  5. var age = 18; // 编译器自动识别为 int
  6. var name = "小帅"; // 编译器自动识别为 string
复制代码

var可以自动识别数据类型。

3.2.2var 使用硬性规则

(1)声明时必须立刻赋值,不能只定义不赋值

  1. //错误用法
  2. var num; // 报错,无法推断类型
  3. num = 10;
  4. //正确用法
  5. var num=10;
复制代码

(2)不能赋值为 null(单独 null 无法推断类型)

  1. var str = null; // 报错,编译器不知道是什么引用类型
  2. // 如需初始null,搭配强制转换
  3. var str = (string)null;
复制代码

(3)同一变量不能多次更换不同类型

类型编译时固定,后续不能存别的类型数据:

  1. var data = 10;
  2. data = "abc"; // 报错,data已经是int类型
复制代码

(4)不能用于方法参数、返回值、类字段

类的成员变量不能用 var;方法参数、返回值不能写 var; var仅能用于方法内部局部变量

  1. // 错误:类字段
  2. class Student
  3. {
  4. var id = 202601; // 编译报错
  5. }
复制代码

(5)搭配匿名类型时必须用 var

匿名类没有自定义类型名,只能依靠 var 接收:

  1. var stu = new { Id = 2390004362, Name = "小帅" };
复制代码

对于(4)、(5)点不完全理解的话,不碍事后续学习后即可。

3.2.3适用场景

(1)类型名很长,简化代码(集合、泛型)

  1. // 冗长显式写法
  2. List<int> list = new List<int>();
  3. // 简洁var写法
  4. var list = new List<int>();
复制代码

(2)接收匿名类型对象(唯一选择)

(3)循环遍历集合

  1. foreach(var item in list)
  2. {
  3. }
复制代码

3.3可空数据类型

3.3.1 作用

值类型(int、double、bool、char 等)默认不能为 null,但业务中存在 “无数据” 场景(比如学生成绩未录入、身高未知),可空类型允许值类型变量存储 null。

3.3.2 两种写法

  1.简写(推荐):值类型?

  1. int? score = null;
  2. double? height = 172.5;
  3. bool? sex = null;
复制代码

   2.完整写法:Nullable

  1. Nullable<int> score = null;
复制代码

3.3.3 核心属性

  • HasValue:bool,判断是否有有效值,true 代表不为 null

  • Value:取出存储的实际值,若为 null 直接调用会报错

  1. int? num = null;
  2. if (num.HasValue)
  3. {
  4. Console.WriteLine(num.Value);
  5. }
复制代码

3.3.4空合并运算符??

简化 null 判断,为空时使用默认值。

  1. int? score = null;
  2. int s = score ?? 0; // score为null则赋值0
复制代码

3.3.5使用限制

仅支持值类型,string、自定义类等引用类型本身就可以存 null,不需要加?。

3.4类型转换

分为隐式转换、显式强制转换、工具类转换三类:

3.4.1 隐式转换

自动转换,无丢失精度,不用写代码;小范围类型 → 大范围类型,编译器自动完成,不会报错 规则:存储空间小 → 存储空间大

  1. int a = 100;
  2. double b = a; // int自动转double,隐式转换
  3. short s = 50;
  4. int i = s;
复制代码

不可隐式:double 转 int、long 转 int、带符号转无符号等(存在精度 / 范围丢失)

3.4.2显式强制转换

格式:(目标类型)变量 适用于大范围转小范围、精度更高转更低;手动强制,可能丢失数据

  1. double d = 99.99;
  2. int num = (int)d; // 结果99,小数直接截断丢失
复制代码

风险:数值超出目标类型范围会溢出,数据错乱。

3.4.3Convert 工具类转换

处理 string 与各类数值转换,常用方法: Convert.ToInt32()、Convert.ToDouble()、Convert.ToChar()、Convert.ToBool();通用,字符串互转首选。

  1. string str = "18";
  2. int age = Convert.ToInt32(str);
复制代码

3.4.4int.Parse / int.TryParse

   Parse:只能用于字符串数字和整形之间转换,转换失败直接抛异常;下面代码的意思是将最后转换的结果存储到a或者b变量。

  1. int a = int.Parse("123");
  2. int b=int.Parse("abc");
复制代码

运行结果如下:

TryParse:只能用于字符串数字和整形之间转换,安全转换,不会报错,返回 bool 判断是否成功。格式  bool 返回结果=int.TryParse(需要转换的数据,out 转换成的数据);如果返回结果为flase,则转换成的数据赋值为0.

  1. //第一种flase情况
  2. string input01= "abc";
  3. int res01;
  4. bool result01 = int.TryParse(input01, out res01);
  5. //第二种ture情况
  6. string input02 = "123";
  7. int res02;
  8. bool result02 = int.TryParse(input02, out res02);
复制代码

运行结果如下:

3.4.5 装箱与拆箱

装箱:值类型转 object(隐式)

  1. int a = 10;
  2. object obj = a; // 装箱
复制代码

拆箱:object 转回值类型(必须显式强制转换)

  1. int b = (int)obj; // 拆箱
复制代码

3.5常量 const

3.5.1定义

常量是运行期间值永远不能修改的变量,使用 const 修饰,编译时就确定值。

3.5.2使用规则

  1. 声明时必须直接赋值,后续不能修改;
  2. 只能是值类型、string,不能是引用类型(除 string);
  3. 属于静态,可直接 类名.常量名 访问;
  4. 不能用 var 定义常量。
  1. // 正确
  2. const string SCHOOL_NAME = "厦门大学";
  3. const int MAX_AGE = 120;
  4. // 错误
  5. const double num; // 未赋值
  6. MAX_AGE = 99; // 常量二次赋值,报错
复制代码

3.5.3 命名规范

常量统一全大写,下划线分隔单词,区分普通变量。

3.5.4const 与 readonly 简单区分

  const:编译期常量,必须声明赋值,全局静态;

  readonly:运行时常量,可在构造函数赋值,实例 / 静态都可用。

四、运算符与表达式

4.1 运算符基础概念

4.1.1 通俗类比

数据相当于原材料(面粉、鸡蛋、学生年龄、分数),运算符是加工数据的动作(搅拌、烘烤、加减对比);计算机依靠运算符处理数据,完成计算、判断、赋值等功能。

4.1.2 专业定义

运算符:告诉编译器执行数学、逻辑操作的专用符号。

  • 操作数:运算符参与运算的数据;

  • 表达式:运算符 + 操作数 组合而成的完整运算式子;

  • 运算结果:表达式执行后产出的值。

  1. int a = 10 + 5;
  2. // 操作数:10、5
  3. // 运算符:+
  4. // 完整表达式:10 + 5
  5. // 运算结果:15
复制代码

4.2 算术运算符

4.2.1 算术运算符清单

运算符作用代码示例运算结果
+加法10 + 515
-减法10 - 55
*乘法10 * 550
/除法10 / 52
%取余(求余数)10 % 31

4.2.2 除法两大核心陷阱

陷阱 1:整数除法直接舍弃小数

两个 int 整数相除,只会保留整数部分,小数直接截断,不四舍五入。

  1. int res1 = 10 / 3; // 输出3,不是3.333
  2. int res2 = 5 / 2; // 输出2,不是2.5
复制代码

解决方案:保证至少一个操作数为浮点型

  1. double r1 = 10.0 / 3; // 3.3333333
  2. double r2 = 10 / 3.0; // 3.3333333
  3. double r3 = (double)10 / 3; // 强制转换后计算
复制代码

陷阱 2:除数不能为 0

整数除以 0 会触发运行时异常 DivideByZeroException,程序直接崩溃。

  1. int a = 10;
  2. int b = 0;
  3. // int c = a / b; // 禁止编写,运行报错
复制代码

4.2.3 取余运算符 % 

% 仅支持整数运算,计算两数相除后的余数。

(1)场景 1:判断数字奇偶

数字 % 2 结果为 0 → 偶数;结果为 1 → 奇数

  1. int num = 7;
  2. int rem = num % 2; // rem=1,奇数
复制代码

(2)场景 2:提取数字个位、十位

  1. int num = 12345;
  2. int u_place = num % 10; // 个位5
  3. int t_place= (num / 10) % 10; // 十位4
复制代码

(3)场景 3:判断能否整除(闰年判断)

  1. int year = 2024;
  2. bool div4 = year % 4 == 0; // 能被4整除 true
  3. bool div100 = year % 100 == 0;
  4. bool div400 = year % 400 == 0;
复制代码

(4)场景 4:时间单位换算(分钟转小时分钟)

  1. int totalMin = 134;
  2. int hour = totalMin / 60; // 2小时
  3. int min = totalMin % 60; // 剩余14分钟
复制代码

4.2.4 算术运算符优先级

数学规则:先乘除取余,后加减;括号优先级最高,可强制改变运算顺序。

  1. int r1 = 5 + 3 * 4; // 5+12=17
  2. int r2 = (5 + 3) * 4; // 8*4=32
复制代码

开发建议:不确定优先级一律加括号,代码可读性更高。


4.3 赋值运算符

赋值运算符作用:将右侧计算结果存入左侧变量。

4.3.1 基础赋值 =

  1. = 不是数学等于,是赋值

  2. 执行顺序:从右向左

  3. 左侧只能是变量,不能是常量、数字、表达式;

  4. 支持链式赋值。

  1. // 声明后赋值
  2. int age;
  3. age = 18;
  4. // 声明并赋值
  5. int score = 95;
  6. // 链式赋值(从右往左)
  7. int a, b, c;
  8. a = b = c = 10; // c=10 → b=10 → a=10
复制代码

4.3.2 复合赋值运算符

简化 “变量自身运算再赋值” 的写法

运算符示例等价完整写法
+=a += 5a = a + 5
-=a -= 3a = a - 3
*=a *= 2a = a * 2
/=a /= 2a = a / 2
%=a %= 3

a = a % 3

  1. int score = 100;
  2. score += 10; // 110
  3. score -= 20; // 90
  4. score *= 2; // 180
  5. score /= 3; // 60
  6. score %= 7; // 4
复制代码

4.3.3 自增、自减运算符

专门用于变量 + 1、变量 - 1,分前置后置两种,核心区别:先运算还是先取值

写法名称等价代码执行逻辑
a++后置自增a = a + 1先使用变量原值,运算结束后再加 1
++a前置自增a = a + 1变量先 + 1,再使用新值
a--后置自减a = a - 1先取值,后减 1
--a前置自减a = a - 1先减 1,后取值

后置 ++ 演示

  1. int a = 5;
  2. int b = a++;
  3. // 执行流程:b=5 → a = a+1
  4. // 结果:a=6,b=5
复制代码

前置 ++ 演示

  1. int x = 5;
  2. int y = ++x;
  3. // 执行流程:x=x+1 → y=6
  4. // 结果:x=6,y=6
复制代码

4.4 关系运算符

作用:对比两个数据的大小、相等关系,运算结果固定为 bool 类型(true/false)

运算符含义示例结果
==等于5 == 5true
!=不等于5 != 3true
>大于5 > 3true
<小于5 < 3false
>=大于等于5 >= 5true
<=小于等于5 <= 3false
  1. int age = 18;
  2. bool isAdult = age >= 18; // true
  3. double score = 85.5;
  4. bool pass = score >= 60; // true
复制代码

4.4.1 两大高频陷阱

陷阱 1:混淆赋值 = 和等于 ==

  1. int num = 5;
  2. bool right = num == 10; // 正确,比较
  3. // bool wrong = num = 10; // 错误,=是赋值,不是比较
复制代码

陷阱 2:浮点数不能直接用 == 判断相等

二进制浮点存储存在精度误差,0.1 + 0.2 不等于精确 0.3 解决方案:判断两数差值小于极小值

  1. double a = 0.1 + 0.2;
  2. double b = 0.3;
  3. double eps = 0.0000001;
  4. bool equal = Math.Abs(a - b) < eps;
复制代码

4.5 逻辑运算符与短路求值

作用:组合多个关系条件,结果依旧是 bool。

4.5.1 运算符说明

运算符名称规则类比
&&逻辑与全部条件 true,结果才 true串联开关,全部打开才通电
||逻辑或任意一个 true,结果 true任意一个 true,结果 true
!逻辑非取反,true 变 false,false 变 true反向开关
  1. int age = 20;
  2. bool hasId = true;
  3. bool canBar = age >= 18 && hasId; // 与,true
  4. bool weekend = true;
  5. bool holiday = false;
  6. bool sleepLate = weekend || holiday; // 或,true
  7. bool rain = false;
  8. bool goodWeather = !rain; // 非,true
复制代码

4.5.2 真值表

逻辑与 &&

ABA && B
truetruetrue
truefalsefalse
falsetruefalse
falsefalsefalse

逻辑或 ||

ABA || B
truetruetrue
truefalsetrue
falsetruetrue
falsefalsefalse

逻辑非!

A!A
truefalse
falsetrue

4.5.3 短路求值(核心重点)

C# && 和 || 具备短路特性:仅靠左侧条件就能确定整体结果时,右侧代码完全不执行

  1. && 左侧为 false → 整体必 false,右侧跳过;

  2. || 左侧为 true → 整体必 true,右侧跳过。

安全判空经典用法

  1. string name = null;
  2. // 左侧name != null 为false,右侧不会执行,不会空指针报错
  3. bool hasText = name != null && name.Length > 0;
复制代码

防止除零崩溃

  1. int a = 5, b = 0;
  2. // b!=0 为false,a/b不会执行,无报错
  3. bool res = (b != 0) && (a / b > 2);
复制代码

4.6 三元条件运算符

4.6.1 语法格式

条件表达式 ? 条件成立返回值 : 条件不成立返回值   逻辑:条件为 true,执行冒号前;false 执行冒号后。

4.6.2 常用场景

场景 1:简单二选一赋值

  1. int age = 20;
  2. string tip = age >= 18 ? "成年人" : "未成年人";
  3. //tip的结果是 “成年人”
复制代码

场景 2:输出内嵌使用

  1. int num = 7;
  2. Console.WriteLine($"{num}是{(num%2==0 ? "偶数" : "奇数")}");
  3. //结果打印 7是奇数
复制代码

场景 3:取最大值、最小值

  1. int a = 10, b = 20;
  2. int max = a > b ? a : b;
  3. int min = a < b ? a : b;
复制代码

场景 4:空值默认文本

  1. string userName = null;
  2. string show = userName != null ? userName : "游客";
复制代码

4.6.3 使用规范建议

适合:简单单层二选一、字符串插值内嵌

禁止:多层嵌套复杂逻辑(可读性极差,改用 if 分支)

禁止:需要执行多行代码(三元只能返回单个值)


4.7 Null 合并运算符

4.7.1 ?? 运算符

作用:左边不为 null 取左边,左边为 null 取右边,简化可空类型判断

  1. int? age = null;
  2. // 传统三元写法
  3. int r1 = age.HasValue ? age.Value : 18;
  4. // ??简化写法
  5. int r2 = age ?? 18;
复制代码

支持链式调用:

  1. string input = null;
  2. string cfg = null;
  3. string res = input ?? cfg ?? "默认文字";
复制代码

4.7.2 ??= C 

#8.0 新增,左边变量为 null 时,才将右侧值赋值给变量;不为 null 则不执行。

  1. string name = null;
  2. name ??= "默认姓名"; // name为null,赋值成功
  3. name = "张三";
  4. name ??= "默认姓名"; // name不为null,无变化
复制代码

4.8 运算符优先级

  1. 后缀自增 / 自减 a++ a--

  2. 前缀自增 / 自减 ++a --a

  3. 乘除取模 * / %

  4. 加减 + -

  5. 大小关系 < <= > >=

  6. 相等判断 == !=

  7. 逻辑与 &&

  8. 逻辑或 ||

  9. null 合并 ??

  10. 三元条件 ?:

  11. 赋值运算符 = += -= *= /= %= ??=

记忆技巧:不用死记,不确定优先级全部加括号。

五、流程控制

5.1 流程控制基础概述

5.1.1 流程控制的核心概念

通俗理解:生活中处处存在判断与重复:下雨带伞、堵车绕路、一日三餐重复进行。程序同理,不会永远从上到下直线执行,需要根据条件选择路径、根据需求重复执行代码。 流程控制就像程序的 “交通指挥系统”,指挥代码按指定的顺序、分支、次数执行。

正式定义:流程控制是通过特定语法语句,控制程序代码的执行顺序与执行路径的机制。没有流程控制的程序只能顺序执行,无法处理复杂业务逻辑;借助流程控制,程序可以实现条件判断、重复执行、分支跳转等复杂功能。

5.1.2 程序三大执行结构

所有程序逻辑都可以拆解为三种基础结构的组合:

结构类型执行规则生活类比典型应用
顺序结构代码从上到下逐行依次执行,无分支、无重复按菜谱步骤做菜,一步接一步变量声明、简单计算、顺序输出
选择结构根据条件判断结果,选择不同代码路径执行十字路口红绿灯,绿灯直行红灯停成绩评级、权限判断、状态校验
循环结构满足条件时,重复执行同一段代码操场跑圈,跑完指定圈数停止遍历数据、重复输入、批量计算

5.2 选择结构

选择结构也叫分支结构,核心是 “满足条件才执行”,根据条件数量与分支数量分为单分支、双分支、多分支三类。

5.2.1 if 单分支语句

语法格式

  1. if (布尔条件表达式)
  2. {
  3. // 条件为 true 时执行的代码块
  4. }
复制代码

执行逻辑

  1. 计算 if 括号内的表达式,得到 true 或 false;
  2. 结果为 true:执行花括号内的代码;
  3. 结果为 false:跳过整个 if 代码块,继续执行后续代码。

代码示例

  1. int score = 85;
  2. if (score >= 60)
  3. {
  4. Console.WriteLine("恭喜你,考试及格了!");
  5. }
  6. Console.WriteLine("程序执行结束");
复制代码

运行结果:

  1. 恭喜你,考试及格了!
  2. 程序执行结束
复制代码

适用场景

只需要处理 “条件成立” 的情况,条件不成立时无需任何操作。

5.2.2 if-else 双分支语句

语法格式

  1. if (布尔条件表达式)
  2. {
  3. // 条件为 true 执行
  4. }
  5. else
  6. {
  7. // 条件为 false 执行
  8. }
复制代码

执行逻辑

条件成立走 if 分支,不成立走 else 分支,两条路径二选一,必然执行其中一个。

代码示例

  1. int age = 15;
  2. if (age >= 18)
  3. {
  4. Console.WriteLine("你已经成年了");
  5. }
  6. else
  7. {
  8. Console.WriteLine("你还是未成年人");
  9. }
复制代码

运行结果:

  1. 你还是未成年人
复制代码

适用场景

典型的二选一逻辑,例如及格 / 不及格、成年 / 未成年、登录成功 / 登录失败。

5.2.3 if-else if-else 多分支语句

语法格式

  1. if (条件1)
  2. {
  3. // 条件1成立执行
  4. }
  5. else if (条件2)
  6. {
  7. // 条件1不成立 且 条件2成立执行
  8. }
  9. else if (条件3)
  10. {
  11. // 条件1、2都不成立 且 条件3成立执行
  12. }
  13. else
  14. {
  15. // 所有条件都不成立执行
  16. }
复制代码

执行逻辑

从上到下依次判断每个条件:

  1. 遇到第一个成立的条件,执行对应代码块,执行完毕后直接跳出整个分支结构;

  2. 所有条件都不成立,执行最后的 else 分支;

  3. 条件顺序至关重要,一旦前面的条件命中,后面的条件不再判断。

代码示例(成绩五级评级)

  1. int score = 85;
  2. if (score >= 90)
  3. {
  4. Console.WriteLine("优秀");
  5. }
  6. else if (score >= 80)
  7. {
  8. Console.WriteLine("良好");
  9. }
  10. else if (score >= 70)
  11. {
  12. Console.WriteLine("中等");
  13. }
  14. else if (score >= 60)
  15. {
  16. Console.WriteLine("及格");
  17. }
  18. else
  19. {
  20. Console.WriteLine("不及格,继续努力");
  21. }
复制代码

运行结果:

  1. 良好
复制代码

注意:85 同时满足>=80和>=70,但因为>=80写在前面,优先命中,不会再判断后续条件。

5.2.4 if 语句使用注意事项与常见陷阱

1. 花括号省略问题

如果 if / else 后只有一条语句,可以省略花括号。

  1. int age = 20;
  2. if (age >= 18)
  3. Console.WriteLine("成年人");
  4. else
  5. Console.WriteLine("未成年人");
复制代码

开发规范建议:即使只有一条语句,也始终书写花括号。

  • 优点:代码结构清晰,后续新增代码时不会出现逻辑错误;

  • 风险:省略花括号后,新增第二行代码时,会被识别为 if 块外的代码,导致逻辑异常。

2. 悬空 else 问题

else 永远与离它最近的、未匹配的 if 进行配对,和代码缩进无关。

  1. int x = 5;
  2. int y = 10;
  3. if (x > 0)
  4. if (y > 0)
  5. Console.WriteLine("x和y都大于0");
  6. else
  7. Console.WriteLine("y不大于0");
复制代码

上述代码中,else 实际匹配内层的 if(y>0),而非外层 if(x>0),最终打印:x和y都大于0。 解决方案:始终使用花括号明确层级,避免歧义。

3. 赋值与等于混淆

= 是赋值运算符,== 是相等比较运算符,二者完全不同。

  1. int num = 5;
  2. // 错误写法:= 是赋值,不是比较,C#中不允许int值直接作为if条件
  3. // if (num = 10) { }
  4. // 正确写法
  5. if (num == 10)
  6. {
  7. Console.WriteLine("num等于10");
  8. }
复制代码

5.2.5 三元表达式与 if-else 的选型对比

两种写法代码对比

  1. int age = 20;
  2. string result1;
  3. // if-else 写法(语句,无返回值)
  4. if (age >= 18)
  5. {
  6. result1 = "成年人";
  7. }
  8. else
  9. {
  10. result1 = "未成年人";
  11. }
  12. // 三元表达式写法(表达式,有返回值)
  13. string result2 = age >= 18 ? "成年人" : "未成年人";
复制代码

本质区别

  • if-else:属于语句,没有返回值,可以执行多行代码;

  • 三元表达式 ?::属于表达式,必须有返回值,只能返回一个结果。

选型对照表

场景推荐方案原因
给单个变量赋值三元表达式代码简洁,一行完成
需要执行多条语句if-else三元只能返回值,不能执行多语句
逻辑复杂、多层分支if-else可读性强,易维护
字符串插值内嵌使用三元表达式可直接嵌入字符串,无需额外变量

内嵌示例:

  1. int score = 85;
  2. Console.WriteLine($"考试结果:{(score >= 60 ? "及格" : "不及格")}");
复制代码

5.2.6 switch 多路分支语句

switch 专门用于 “一个变量匹配多个固定值” 的多分支场景,比多层 if-else 结构更清晰。

语法格式

  1. switch (待匹配的表达式)
  2. {
  3. case 常量值1:
  4. // 匹配值1时执行的代码
  5. break;
  6. case 常量值2:
  7. // 匹配值2时执行的代码
  8. break;
  9. default:
  10. // 所有case都不匹配时执行
  11. break;
  12. }
复制代码

执行逻辑

  1. 计算 switch 括号内表达式的值;

  2. 从上到下依次与 case 后的常量值对比;

  3. 匹配成功则执行对应 case 内的代码,遇到 break 跳出整个 switch

  4. 全部不匹配则执行 default 分支。

代码示例(星期查询)

  1. Console.Write("请输入星期几(1-7):");
  2. int day = int.Parse(Console.ReadLine());
  3. switch (day)
  4. {
  5. case 1:
  6. Console.WriteLine("星期一:新的一周开始啦!");
  7. break;
  8. case 2:
  9. Console.WriteLine("星期二:加油!");
  10. break;
  11. case 3:
  12. Console.WriteLine("星期三:小周末");
  13. break;
  14. case 4:
  15. Console.WriteLine("星期四:快周末了");
  16. break;
  17. case 5:
  18. Console.WriteLine("星期五:明天就周末了!");
  19. break;
  20. case 6:
  21. Console.WriteLine("星期六:休息日");
  22. break;
  23. case 7:
  24. Console.WriteLine("星期日:明天又要上班了");
  25. break;
  26. default:
  27. Console.WriteLine("输入错误,请输入1-7之间的数字");
  28. break;
  29. }
复制代码

5.2.7 switch 穿透规则与语法规范

核心规则

C# 不允许有代码的 case 自动向下穿透,每个带代码的 case 必须以 break、return 等跳转语句结尾,否则编译报错。

合法穿透:空 case 共享代码

多个空 case 可以堆叠,共享同一段执行代码,实现 “多值匹配同一逻辑”。

  1. int score = 85;
  2. int scoreLevel = score / 10;
  3. switch (scoreLevel)
  4. {
  5. case 10:
  6. case 9:
  7. Console.WriteLine("优秀"); // 90-100分共用
  8. break;
  9. case 8:
  10. case 7:
  11. Console.WriteLine("良好"); // 70-89分共用
  12. break;
  13. case 6:
  14. Console.WriteLine("及格");
  15. break;
  16. default:
  17. Console.WriteLine("不及格");
  18. break;
  19. }
复制代码

语法要求

  1. case 后必须是常量,不能是变量或范围表达式;

  2. default 分支可选,不是必须书写;

  3. 支持匹配的类型:整数、字符、字符串、枚举等。

5.2.8 if 与 switch 的场景选型指南

场景推荐方案原因
条件复杂(范围判断、多条件组合)ifif 支持任意布尔表达式,灵活度最高
条件简单(单个变量匹配固定离散值)switch结构清晰,分支多时执行效率更高
分支数量 2-3 个if写法简单,无需额外语法
分支数量大于 5 个switch层级清晰,维护成本低

5.3 循环结构

循环结构用于重复执行同一段代码,避免重复书写,提升代码复用性。C# 提供 while、do-while、for 三种基础循环,以及循环控制语句。

5.3.1 while 循环:先判断后执行

语法格式

  1. while (布尔循环条件)
  2. {
  3. // 循环体:条件为true时重复执行
  4. }
复制代码

执行逻辑

  1. 判断循环条件,得到 true/false;

  2. 条件为 true:执行循环体,执行完毕后回到第 1 步再次判断;

  3. 条件为 false:结束循环,执行循环后续代码。

代码示例(输出 1 到 5)

  1. int i = 1;
  2. while (i <= 5)
  3. {
  4. Console.WriteLine(i);
  5. i++; // 循环变量自增,必须修改条件,否则会死循环
  6. }
  7. Console.WriteLine("循环结束");
复制代码

运行结果:

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 循环结束
复制代码

死循环

循环条件永远为 true,程序无限执行无法退出,称为死循环。

  1. // 死循环示例,实际开发禁止
  2. while (true)
  3. {
  4. Console.WriteLine("无限循环中...");
  5. }
复制代码

可通过 break 语句主动退出死循环:

  1. int i = 1;
  2. while (true)
  3. {
  4. Console.WriteLine($"第{i}次循环");
  5. if (i >= 5)
  6. {
  7. break; // 满足条件时跳出循环
  8. }
  9. i++;
  10. }
复制代码

5.3.2 do-while 循环:先执行后判断

语法格式

  1. do
  2. {
  3. // 循环体
  4. } while (循环条件); // 注意末尾必须有分号
复制代码

执行逻辑

  1. 先执行一次循环体;

  2. 再判断循环条件,为 true 则继续循环,为 false 则结束。 核心特点:循环体至少执行一次。

代码示例(猜数字简化版)

  1. Random random = new Random();
  2. int secretNumber = random.Next(1, 11);
  3. int guess;
  4. do
  5. {
  6. Console.Write("猜一个数字(1-10):");
  7. guess = int.Parse(Console.ReadLine());
  8. if (guess > secretNumber)
  9. Console.WriteLine("猜大了");
  10. else if (guess < secretNumber)
  11. Console.WriteLine("猜小了");
  12. } while (guess != secretNumber);
  13. Console.WriteLine("恭喜你猜对了!");
复制代码

5.3.3 while 与 do-while 核心区别对比

对比项while 循环do-while 循环
判断时机先判断条件,后执行循环体先执行循环体,后判断条件
最少执行次数0 次(条件一开始不成立则一次都不执行)1 次(无论条件如何,至少执行一次)
语法特点while 后无分号while 条件后必须加分号
适用场景可能一次都不需要执行的场景至少执行一次的场景,如菜单、用户输入校验

5.3.4 for 循环:计数型循环

for 循环是最常用的循环,专门用于已知循环次数的场景,语法上将初始化、条件、迭代集中在一起,结构清晰。

语法格式

  1. for (初始化表达式; 循环条件; 迭代表达式)
  2. {
  3. // 循环体
  4. }
复制代码

三部分说明

组成部分

执行时机

作用

示例

初始化表达式

循环开始前执行,仅执行 1 次

声明并初始化循环变量

int i = 1

循环条件

每次循环开始前判断

控制循环是否继续

i <= 10

迭代表达式

每次循环体执行完毕后执行

修改循环变量,通常自增 / 自减

i++

代码示例(输出 1 到 10)

  1. for (int i = 1; i <= 10; i++)
  2. {
  3. Console.WriteLine(i);
  4. }
复制代码

5.3.5 for 循环执行流程分步拆解

以 for (int i = 1; i <= 3; i++) 为例,完整执行步骤:

  1. 初始化:执行 int i = 1,循环变量 i 初始化为 1,仅执行一次;
  2. 条件判断:判断 1 <= 3,结果为 true;
  3. 执行循环体:输出第 1 次循环;
  4. 迭代更新:执行 i++,i 变为 2;
  5. 条件判断:判断 2 <= 3,结果为 true;
  6. 执行循环体:输出第 2 次循环;
  7. 迭代更新:执行 i++,i 变为 3;
  8. 条件判断:判断 3 <= 3,结果为 true;
  9. 执行循环体:输出第 3 次循环;
  10. 迭代更新:执行 i++,i 变为 4;
  11. 条件判断:判断 4 <= 3,结果为 false,循环结束。

5.3.6 for 循环的多种变体用法

(1)倒序循环

  1. // 从10输出到1
  2. for (int i = 10; i >= 1; i--)
  3. {
  4. Console.WriteLine(i);
  5. }
复制代码

(2)自定义步长

  1. // 输出10以内的偶数
  2. for (int i = 2; i <= 10; i += 2)
  3. {
  4. Console.WriteLine(i);
  5. }
复制代码

(3)多循环变量

  1. for (int i = 1, j = 10; i <= 5; i++, j--)
  2. {
  3. Console.WriteLine($"i={i}, j={j}");
  4. }
复制代码

(4)省略写法

for 循环的三个部分都可以省略,但分号必须保留。

  1. // 等同于 while(true) 死循环
  2. for (;;)
  3. {
  4. Console.WriteLine("死循环");
  5. }
复制代码

5.3.7 循环控制语句:break、continue、return

(1)break:跳出当前循环

作用:立即终止当前所在的整个循环,执行循环后续代码。

  1. for (int i = 1; i <= 10; i++)
  2. {
  3. if (i == 5)
  4. {
  5. Console.WriteLine("找到5了,停止循环");
  6. break; // 直接结束整个for循环
  7. }
  8. Console.WriteLine($"当前数字:{i}");
  9. }
复制代码

注意:嵌套循环中,break 只能跳出它所在的那一层循环。

(2)continue:跳过本次循环

作用:结束当前这一次循环,直接进入下一次循环判断,不执行本次循环剩余代码。

  1. for (int i = 1; i <= 10; i++)
  2. {
  3. if (i % 4 == 0)
  4. {
  5. continue; // 遇到4的倍数,跳过本次输出
  6. }
  7. Console.WriteLine(i);
  8. }
复制代码

运行结果: 输出 1、2、3、5、6、7、9、10,跳过 4 和 8。

(3)return:退出整个方法

作用:直接终止当前方法的执行,不仅仅跳出循环,方法内后续所有代码都不再执行。

  1. static void PrintNumbers()
  2. {
  3. for (int i = 1; i <= 10; i++)
  4. {
  5. if (i == 5)
  6. {
  7. Console.WriteLine("遇到5,方法结束");
  8. return; // 退出整个方法
  9. }
  10. Console.WriteLine(i);
  11. }
  12. // 这行代码永远不会执行
  13. Console.WriteLine("循环结束");
  14. }
复制代码

5.3.8 goto 跳转语句(了解即可)

语法与作用

goto 可以直接跳转到代码中指定标签的位置,属于无条件跳转语句。

  1. goto 标签名;
  2. // 被跳过的代码
  3. 标签名:
  4. // 跳转后执行的代码
复制代码

典型场景:跳出多层嵌套循环

  1. // 寻找第一个乘积大于50的数对
  2. for (int i = 1; i <= 10; i++)
  3. {
  4. for (int j = 1; j <= 10; j++)
  5. {
  6. int product = i * j;
  7. if (product > 50)
  8. {
  9. Console.WriteLine($"找到:{i}×{j}={product}");
  10. goto Found; // 直接跳出两层循环
  11. }
  12. }
  13. }
  14. Found:
  15. Console.WriteLine("查找结束");
复制代码

使用规范

  1. 99% 场景不推荐使用:goto 会破坏代码结构,降低可读性,几乎都可以用 break、continue、return 替代;

  2. 唯一推荐场景:跳出深层嵌套循环(3 层及以上);

  3. 禁止用法:不能跳进循环内部,不能跳过变量初始化。

5.3.9 循环嵌套与多层循环执行逻辑

一个循环内部包含另一个循环,称为循环嵌套。外层循环执行一次,内层循环完整执行一轮。

(1)示例 1:九九乘法表

  1. for (int i = 1; i <= 9; i++) // 外层:控制行数
  2. {
  3. for (int j = 1; j <= i; j++) // 内层:控制每行的列数
  4. {
  5. Console.Write($"{j}×{i}={i * j}\t");
  6. }
  7. Console.WriteLine(); // 每行结束换行
  8. }
复制代码

(2)示例 2:直角三角形星号图案

  1. int rows = 5;
  2. for (int i = 1; i <= rows; i++) // 外层:行数
  3. {
  4. for (int j = 1; j <= i; j++) // 内层:每行的*数量
  5. {
  6. Console.Write("*");
  7. }
  8. Console.WriteLine();
  9. }
复制代码

运行结果:

  1. *
  2. **
  3. ***
  4. ****
  5. *****
复制代码

执行规律

  • 外层循环控制 “行”,内层循环控制 “列”;
  • 外层循环执行 1 次,内层循环从头到尾执行完整一轮;
  • 总执行次数 = 外层次数 × 内层次数。


六、选择循环知识点掌握清单与速查表

6.1 知识点掌握清单

类别具体内容掌握星级
if 分支语句if 单分支、if-else 双分支、多分支 if⭐⭐⭐⭐⭐
switch 分支语句基础语法、穿透规则、default 用法⭐⭐⭐⭐
while 循环基础语法、死循环、break/continue⭐⭐⭐⭐⭐
do-while 循环语法、与 while 的核心区别⭐⭐⭐⭐
for 循环语法、执行流程、变体用法⭐⭐⭐⭐⭐
循环嵌套多层循环逻辑、图案打印、九九乘法表⭐⭐⭐⭐
跳转语句break、continue、return 的区别与用法⭐⭐⭐⭐⭐
goto 语句基础语法、使用场景与规范⭐⭐
switch 表达式C# 8.0 + 新语法、模式匹配⭐⭐⭐⭐

6.2语法速查表

分支语句

语法说明
if (条件) { }单分支
if (条件) { } else { }双分支
if (条件1) { } else if (条件2) { } else { }多分支
switch(表达式) { case 值: 代码; break; default: 代码; break; }switch 多路分支

循环语句

语法说明
while (条件) { }先判断后执行
do { } while (条件);先执行后判断,至少一次
for (初始化; 条件; 迭代) { }计数型循环

跳转语句

语句作用
break;跳出当前循环 /switch
continue;结束本次循环,进入下一轮
return;退出当前方法
goto 标签;跳转到指定标签(慎用)

希望本文对大家有所帮助,如有问题,欢迎各位博友积极指出!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2026 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行