一、变量
在入门要求下,C#【1】 中的变量类型可大致分为 整数、小数、字符类、布尔类型四大类。
1.变量类型
1.整数类型
整数类型变量又可分为有符号整数、无符号整数两类,以下是详细区分
| 类型 | 位数 | 取值范围 |
|---|---|---|
sbyte | 8位 | -128 ~ 127 |
byte | 8位 | 0 ~ 255 |
short | 16位 | -32,768 ~ 32,767 |
ushort | 16位 | 0 ~ 65,535 |
int | 32位 | 约 -21亿 ~ 21亿 |
uint | 32位 | 0 ~ 约42亿 |
long | 64位 | 很大的正负整数 |
ulong | 64位 | 很大的非负整数 |
对于取值范围可以从数字位数的角度理解,比如int【2】类型,总共有32位,最左边第一位数字代表符号位(1为负、0为正),剩下31位可用于表示数字,因此其数字范围是正负231-1。
例如:
byte age = 18;
int population = 1_400_000_000;
long distance = 9_000_000_000L;
uint count = 100U;
在实际游戏开发中,通常先使用int;数字超过int范围后再使用long【3】类型。
2.小数类型
小数类型可分为二进制浮点数和高精度十进制数字
| 类型 | 位数 | 有效精度 | 适合场景 |
|---|---|---|---|
float | 32位 | 约6~9位数字 | 图形、游戏等 |
double | 64位 | 约15~17位数字 | 普通小数计算,默认选择 |
decimal | 128位 | 约28~29位数字 | 金额、财务等高精度计算 |
float height = 1.75F; // 必须加 F
double pi = 3.1415926535; // 小数字面量默认是 double
decimal price = 99.99M; // 必须加 M
float 和 double 可能产生小数误差:
double result = 0.1 + 0.2; // 可能不是精确的 0.3
涉及金钱时通常使用 decimal
3.字符类型
char 表示一个 UTF-16【4】 编码单元,使用单引号:
char grade = 'A';
char chinese = '中';
char newLine = '\n';
注意:部分 emoji 需要两个 char 才能表示,因此 char 不一定等同于用户看到的一个完整字符。
string 表示零个或多个字符组成的文本,使用双引号:
string name = "张三";
string message = "Hello, C#!";
string empty = "";
string 和 char 的区别:
char letter = 'A'; // 单引号,一个字符
string word = "Apple"; // 双引号,一串字符
4. 布尔类型
布尔类型只有 true 和 false 两个值:
bool isStudent = true;
bool isFinished = false;
其主要作用是用于条件判断
2.变量的本质
1.定义与特征
变量就是:一个有名称、有类型、能够保存数据的位置。一个变量通常具有:
- 类型:可以保存什么数据。
- 名称:如何访问这个变量。
- 值:当前保存的数据。
- 作用域:可以在哪些位置使用。
if (true)
{
int score = 100;
Console.WriteLine(score); // 可以使用
}
// Console.WriteLine(score); // 错误,超出作用域
2.值类型与引用类型
变量除了通过数据类型分类外,还可以通过值类型【5】和引用类型【6】
整数、小数、布尔和字符通常属于值类型:
int number1 = 10;
int number2 = number1;
number2 = 20;
Console.WriteLine(number1); // 10
Console.WriteLine(number2); // 20
赋值时会复制一份值,所以修改 number2 不会影响 number1。
常见值类型包括:
int、long、float、double、decimal、bool、char、struct、enum
字符串、数组【7】、类等属于引用类型。变量中保存的是对象的引用:
int[] numbers1 = { 1, 2, 3 };
int[] numbers2 = numbers1;
numbers2[0] = 100;
Console.WriteLine(numbers1[0]); // 100
常见引用类型包括:
string、数组、class、interface、object、delegate
string 虽然是引用类型,但它具有不可变性,不能直接修改已有字符串的内容。
string name = "张";
name = name + "三"; // 实际上产生了一个新的字符串
3.命名规则
C# 变量名必须符合以下基本规则:
- 可以使用字母、数字和下划线。
- 不能以数字开头。
- 不能包含空格以及
-、+、%等符号。 - 不能直接使用 C# 关键字。
- C# 严格区分大小写。
二、常量
常量是一个在声明后不能再被修改的固定值。C# 中主要使用 const 声明编译期常量
1.基本语法
const【8】 数据类型 常量名称 = 常量值;
例如:
const int MaximumScore = 100;
const double Pi = 3.1415926;
const string DefaultName = "游客";
const bool IsDebugMode = false;
//常量必须在声明时赋值:
const int MaximumScore = 100; // 正确
// const int MinimumScore; // 错误:没有初始化
// MinimumScore = 0;
//赋值后不能修改
const int MaximumScore = 100;
// MaximumScore = 200; // 编译错误
2.const的主要特点
1.必须在声明时初始化。2.初始化值必须能在编译期间确定。3.声明后不能重新赋值。4.可以声明为局部常量或类成员常量。5.类中的 const 成员隐式属于类本身,相当于静态成员。6.不能同时写成 static const。
class MathSettings
{
public const double Pi = 3.1415926;
}
double result = MathSettings.Pi * 10;
//下面写法错误:
// public static const double Pi = 3.14;
3.常用的常量类型
通常,以上变量类型都可作为常量定义,运行编译才可确定的数值,不可作为常量:
const byte Level = 10;
const int MaximumCount = 100;
const long Distance = 9_000_000_000L;
const float Rate = 0.5F;
const double Pi = 3.1415926;
const decimal TaxRate = 0.13M;
const char Separator = ',';
const string DefaultName = "游客";
const bool IsEnabled = true;
//枚举值也可以作为常量:
const ConsoleColor DefaultColor = ConsoleColor.Blue;
//常量表达式可以使用其他常量:
const int SecondsPerMinute = 60;
const int MinutesPerHour = 60;
const int SecondsPerHour = SecondsPerMinute * MinutesPerHour;
4.常量的作用域
局部常量只能在所在代码块中使用,类成员常量可以通过类名访问:
void Calculate()
{
const double Pi = 3.1415926;
double area = Pi * 10 * 10;
}
// 此处不能使用 Pi
class GameSettings
{
public const int MaximumLevel = 100;
}
Console.WriteLine(GameSettings.MaximumLevel);
三、C# 转义字符
转义字符【9】用于表示一些不能直接输入,或者具有特殊含义的字符。它们通常以反斜杠 \ 开头。
1.常用转义字符
| 转义字符 | 含义 | 示例 |
|---|---|---|
\n | 换行 | "第一行\n第二行" |
\r | 回车 | "内容\r" |
\t | 水平制表符 | "姓名\t成绩" |
\\ | 反斜杠 | "C:\\Users\\Li" |
\" | 双引号 | "他说:\"你好\"" |
\' | 单引号 | '\'' |
\0 | 空字符,Unicode【10】 U+0000 | '\0' |
\a | 警报、响铃 | "\a" |
\b | 退格 | "ABC\b" |
\f | 换页 | "\f" |
\v | 垂直制表符 | "\v" |
\e | Escape 字符 | "\e",C# 13+ |
2.常见示例
换行:
string message = "姓名:张三\n年龄:18";
Console.WriteLine(message);
制表符:
Console.WriteLine("姓名\t成绩");
Console.WriteLine("张三\t100");
Console.WriteLine("李四\t90");
双引号:
string sentence = "老师说:\"请认真学习。\"";
反斜杠:
string path = "C:\\Users\\Li\\Documents";
单引号字符:
char singleQuote = '\'';
3.Unicode 转义字符
可以通过 Unicode 编码表示字符。
\u 后面必须跟四位十六进制数字:
char letter = '\u0041'; // A
char chinese = '\u4E2D'; // 中
string text = "\u4F60\u597D"; // 你好
\U 后面跟八位十六进制数字,可以表示 emoji 等较大的 Unicode 字符:
string emoji = "\U0001F600"; // 😀
\x 后面可以跟十六进制数字:
char letter = '\x41'; // A
由于 \x 使用的数字位数可能不固定,容易产生歧义,因此通常优先使用固定四位的 \u
4.逐字字符串 @与 原始字符串
在字符串前添加 @,反斜杠会按照普通字符处理,适合表示文件路径:
string path1 = "C:\\Users\\Li\\Documents";
string path2 = @"C:\Users\Li\Documents";
这两个字符串的内容相同。
逐字字符串【11】中的双引号需要写两次:
string text = @"老师说:""你好。""";
在逐字字符串中,\n 不会被解释为换行:
string text = @"第一行\n第二行";
Console.WriteLine(text);
输出:
第一行\n第二行
C# 11 开始支持三个双引号组成的原始字符串【12】,适合保存 JSON、HTML 等文本:
string json = """
{
"name": "张三",
"path": "C:\Users\Li"
}
""";
四、数据类型的转换
类型转换主要分为两类:隐式转换【13】和显式转换【14】。
1.隐式转换
范围较小的类型转换为范围较大的类型,编译器自动完成,通常不会丢失数据。
int damage = 10;
float finalDamage = damage;
int 转 float:
int speed = 5;
float distance = speed * Time.deltaTime;
2. 显式转换
强制转换 (类型)
可能丢失数据,需要使用强制转换符:
float distance = 9.8f;
int value = (int)distance; // 9,直接截掉小数
Unity 中常用于坐标、时间或下标:
int x = (int)transform.position.x;
int second = (int)Time.time;
不能直接将字符串强转为整数:
// int level = (int)"10"; // 错误
Parse【15】
用于将字符串转换成指定类型:
int level = int.Parse("10");
float speed = float.Parse("5.5");
bool enabled = bool.Parse("true");
格式错误时会抛出异常,因此不适合直接处理玩家输入。可使用TryParse【16】替代:
if (int.TryParse(inputField.text, out int level))
{
Debug.Log($"等级:{level}");
}
else
{
Debug.LogWarning("请输入正确的数字");
}
// bool 是否成功 = 数据类型.TryParse(字符串, out 转换结果);
//其他类型:
float.TryParse("5.5", out float speed);
double.TryParse("3.14", out double pi);
bool.TryParse("true", out bool enabled);
Convert【18】:
可以在字符串和常见基础类型之间转换:
int level = Convert.ToInt32("10");
float speed = Convert.ToSingle("5.5");
string text = Convert.ToString(100);
注意强转与 Convert 的区别:
int a = (int)3.8f; // 3,截掉小数
int b = Convert.ToInt32(3.8f); // 4,四舍五入
五、C# 异常捕获
异常捕获【19】用于处理程序运行期间出现的错误,避免程序直接中断。
基本语法
try
{
// 可能出现异常的代码
}
catch (Exception ex)
{
// 出现异常后的处理
}
finally
{
// 无论是否异常,通常都会执行
}
//示例
try
{
int number = int.Parse("abc");
}
catch (Exception ex)
{
Debug.LogError($"转换失败:{ex.Message}");
}
六、运算
运算可分为算数运算、逻辑运算【20】、位运算【21】几类。
1.算数运算符
| 运算符 | 含义 | 示例 |
|---|---|---|
+ | 加法 | a + b |
- | 减法 | a - b |
* | 乘法 | a * b |
/ | 除法 | a / b |
% | 取余 | a % b |
++ | 自增 1 | count++ |
-- | 自减 1 | count-- |
int a = 10;
int b = 3;
Debug.Log(a + b); // 13
Debug.Log(a - b); // 7
Debug.Log(a * b); // 30
Debug.Log(a / b); // 3
Debug.Log(a % b); // 1
//整数相除会舍弃小数:
float result1 = 5 / 2; // 2
float result2 = 5f / 2f; // 2.5
//复合赋值运算符:
hp += 10; // hp = hp + 10
hp -= 10;
hp *= 2;
hp /= 2;
hp %= 3;
//++ 前置与后置:
int a = 10;
int b = a++; // b是10,然后a变成11
int c = ++a; // a先变成12,然后赋给c
Unity 常用数学方法:
Mathf.Abs(value); // 绝对值
Mathf.Min(a, b); // 最小值
Mathf.Max(a, b); // 最大值
Mathf.Clamp(hp, 0, 100); // 限制范围
Mathf.Pow(2, 3); // 2的3次方
Mathf.Sqrt(16); // 平方根
Mathf.RoundToInt(3.6f); // 四舍五入
Mathf.FloorToInt(3.6f); // 向下取整
Mathf.CeilToInt(3.1f); // 向上取整
Mathf.Lerp(0, 100, 0.5f); // 插值,结果50
随机数:
int number = Random.Range(0, 10); // 0~9
float value = Random.Range(0f, 10f); // 浮点范围
//复杂运算建议使用括号明确顺序:
float damage = (attack + weaponDamage) * multiplier; 若无括号则是先算乘除后算加减
2. 字符串拼接与打印
使用 + 拼接
string playerName = "张三";
int score = 100;
string message = "玩家:" + playerName + ",分数:" + score;
字符串插值
Unity 开发中推荐使用:
string message = $"玩家:{playerName},分数:{score}";
设置小数显示位数:
float time = 12.3456f;
Debug.Log($"时间:{time:F2}"); // 时间:12.35
其他拼接方法
string result1 = string.Concat("生命值:", hp);
string result2 = string.Join(", ", playerNames);
string result3 = string.Format("分数:{0}", score);
Console.WriteLine("分数:{0}", score); //格式化打印
打印方法
普通 C#:
Console.Write("不换行");
Console.WriteLine("打印后换行");
Unity:
Debug.Log("普通信息");
Debug.LogWarning("警告信息");
Debug.LogError("错误信息");
Debug.LogException(exception);
显示到 UI:
scoreText.text = $"分数:{score}";
字符串不可变。在 Update() 中频繁拼接和更新 UI 可能产生额外内存分配,最好只在数据改变时更新。
3. 条件运算符(比较运算符)
比较结果都是 bool 类型:
| 运算符 | 含义 |
|---|---|
== | 等于 |
!= | 不等于 |
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
if (hp <= 0)
{
Debug.Log("玩家死亡");
}
判断范围:
if (hp >= 0 && hp <= maxHp)
{
Debug.Log("生命值正常");
}
浮点数可能存在精度误差,Unity 中推荐:
if (Mathf.Approximately(a, b))
{
Debug.Log("两个浮点数近似相等");
}
字符串内容比较:
if (playerName == "张三")
{
}
忽略大小写:
bool same = string.Equals(
input,
"start",
StringComparison.OrdinalIgnoreCase
);
4. 逻辑运算符
| 运算符 | 含义 |
|---|---|
&& | 并且,全部为真才是真 |
| ` | |
! | 取反 |
^ | 异或,两个条件不同时为真 |
if (hp > 0 && hasWeapon)
{
Debug.Log("可以攻击");
}
if (isDead || isPaused)
{
return;
}
if (!isGrounded)
{
Debug.Log("角色在空中");
}
&& 和 || 具有短路功能:
if (player != null && player.Health > 0)
{
}
如果 player 是 null,后面的代码不会执行,因此不会访问空对象。
& 和 | 也能用于布尔值,但不会短路,普通条件判断优先使用 && 和 ||。
5. 位运算符
位运算直接操作整数的二进制位:
| 运算符 | 含义 |
|---|---|
& | 按位与 |
| ` | ` |
^ | 按位异或 |
~ | 按位取反 |
<< | 左移 |
>> | 右移 |
int a = 6; // 0110
int b = 3; // 0011
int andResult = a & b; // 0010,结果2
int orResult = a | b; // 0111,结果7
int xorResult = a ^ b; // 0101,结果5
位移:
int a = 1 << 3; // 0001 左移3位,结果8
int b = 8 >> 2; // 1000 右移2位,结果2
Unity 中最常见的用途是 LayerMask:
LayerMask targetMask = LayerMask.GetMask("Enemy", "Obstacle");
bool isIncluded =
(targetMask.value & (1 << gameObject.layer)) != 0;
添加和移除某一位:
mask |= 1 << layer; // 添加层
mask &= ~(1 << layer); // 移除层
位运算通常用于层级遮罩、权限和状态标记,不用于普通小数。
6. 三目运算符
语法:
条件 ? 条件成立的值 : 条件不成立的值
示例:
string state = hp > 0 ? "存活" : "死亡";
等价于:
string state;
if (hp > 0)
{
state = "存活";
}
else
{
state = "死亡";
}
Unity 示例:
spriteRenderer.color = isHurt ? Color.red : Color.white;
三目运算符【22】适合简单的二选一。逻辑复杂时使用 if...else。
七、三类逻辑
1. while 循环
只要条件成立,就不断执行:
int count = 0;
while (count < 3)
{
Debug.Log(count);
count++;
}
适合循环次数事先不确定的情况。
无限循环【23】:
while (true)
{
if (shouldStop)
{
break;
}
}
Unity 主线程中不能执行无法及时结束的循环,否则游戏会卡死。跨帧等待应使用协程【24】:
IEnumerator Countdown(float time)
{
while (time > 0f)
{
time -= Time.deltaTime;
yield return null;
}
Debug.Log("倒计时结束");
}
2. for 循环
语法:
for (初始化; 循环条件; 更新变量)
{
// 循环体
}
示例:
for (int i = 0; i < 5; i++)
{
Debug.Log(i);
}
遍历数组和列表【25】:
for (int i = 0; i < enemies.Count; i++)
{
enemies[i].TakeDamage(10);
}
删除列表元素时通常倒序遍历【26】:
for (int i = enemies.Count - 1; i >= 0; i--)
{
if (enemies[i] == null)
{
enemies.RemoveAt(i);
}
}
数组使用 .Length,列表使用 .Count:
array.Length;
list.Count;
3. do...while 循环
先执行一次,再判断条件,所以循环体至少执行一次:
int count = 0;
do
{
Debug.Log(count);
count++;
}
while (count < 3);
注意结尾有分号:
while (条件);
适用于必须先执行一次,再决定是否继续的逻辑。
4. switch
适合根据一个变量的不同值执行不同代码,Unity 中经常用于角色状态。
enum PlayerState
{
Idle,
Run,
Jump
}
switch (playerState)
{
case PlayerState.Idle:
Debug.Log("待机");
break;
case PlayerState.Run:
Debug.Log("奔跑");
break;
case PlayerState.Jump:
Debug.Log("跳跃");
break;
default:
Debug.LogWarning("未知状态");
break;
}
多个情况执行相同逻辑:
switch (playerState)
{
case PlayerState.Idle:
case PlayerState.Run:
Debug.Log("角色在地面");
break;
case PlayerState.Jump:
Debug.Log("角色在空中");
break;
}
较新的 C# 还可以使用 switch 表达式:
string animationName = playerState switch
{
PlayerState.Idle => "Idle",
PlayerState.Run => "Run",
PlayerState.Jump => "Jump",
_ => "Unknown"
};
_ 相当于传统 switch 中的 default。是否支持取决于 Unity 项目使用的 C# 版本。
5. 循环控制关键字
break; // 立即结束当前循环或switch
continue; // 跳过本次循环,进入下一次
return; // 直接结束当前方法
示例:
for (int i = 0; i < enemies.Count; i++)
{
if (enemies[i] == null)
{
continue;
}
if (enemies[i].IsBoss)
{
break;
}
enemies[i].TakeDamage(10);
}
补充:遍历集合还可以使用 foreach:
foreach (Enemy enemy in enemies)
{
enemy.TakeDamage(10);
}
foreach 遍历期间通常不能增加或删除集合元素;需要删除时使用倒序 for。
Comments NOTHING