跳过正文
  1. 文章/

C# 学习加固笔记(一):语法与流程控制

Lickba
作者
Lickba
热爱游戏,也喜欢记录创作过程与踩过的坑。
C# 学习加固 - 这篇文章属于一个选集。
§ 1: 本文

这是「C# 学习加固」系列的第 1 篇,共 5 篇;可通过文末的系列导航继续阅读。

1. 注释以及 log
#

using UnityEngine;

// 注释是写给人看的
public class Hello : MonoBehaviour
{
    void Start()
    {
        Debug.Log("这是一个普通日志。");
        Debug.LogWarning("这是一个警告日志。");
        Debug.LogError("这是一个错误日志。");
    }
}

2. 变量以及常用类型
#

  1. 变量是什么?

    • 数据的抽象表示,类似数学中的变量
    • 可以变化的,值可以变
    • 除了整数、小数等以外还可以表示玩家的攻击力,生命值,等级,游戏昵称或者其他我们自定义的信息
  2. 常用的变量类型
    start(字符串):用来存储用户名、密码、角色名称、技能名称等文本信息

    int(整数):存角色等级、纯数字ID等

    float(浮点数):小数,用来存时间这类对精度要求更高的数值

    整数、浮点数等数值类型都有取值范围,但是大多情况下以及目前情况下,可以忽视这个问题,感兴趣点链接了解:

    Microsoft:C# 整型数值类型

  3. 变量的申明
    类型 变量名 = 变量初始值;

  4. 驼峰命名法

    • 大驼峰:首字母大写,之后每个单次的首字母也大写,其余字母小写,如:GameOver、Home、MyGameID
    • 小驼峰:首字母小写,之后每个单次的首字母也大写,其余字母小写,如:gameOver、home、myGameID
  5. 命名约束

    • 首字母不能是数字,但是后续可以是数字
using UnityEngine;

public class Hello : MonoBehaviour
{
    private void Start()
    {
        //变量的声明:类型 变量名 = 初始值;
        string myName = "Lickba"; //字符串的使用,需要加双引号
        int age = 18;           //非字符串的使用,不需要加引号
        float num = 15.654787354f; //浮点数的使用,后面加f表示这是一个浮点数

        //使用或输出变量的时候,不需要加双引号,f等
        Debug.Log(myName);
        Debug.Log(age);
        Debug.Log(num);
    }
}   

3. 转义字符
#

  1. 转义字符

    转义字符\
    单引号'
    双引号"
    退格符 (unity不支持)\b
    换页符(unity不支持)\f
    换行符\n
    回车\r
    表格 水平\t
    表格 垂直\v
  2. 原义符

    用来取消转义字符的特殊意义,要写在双引号外面

    using UnityEngine;
    
    public class Hello : MonoBehaviour
    {
        private void Start()
        {
            // 转义字符是针对字符串的
            string myName = "张三\t李四\t王五";
    
            // 特殊需求:我们希望输出 双引号
            // 程序认为 双引号是特殊符号,包括起来的就是字符串
            // \:转义字符,它本身也是一个特殊符号
            string str = "\"";
            Debug.Log(myName);
    
            // 输出转义字符的方式,就是用转义字符去修饰它
            Debug.Log("\\\\");
    
            // @:原义符,取消转义字符的作用
            // 不能直接输出双引号,两个双引号会被认为是一个双引号
            string names2 = @"张三\t李四\t王五";
            string names3 = @"张三\t李四\t王五""";
            Debug.Log(names2);
            Debug.Log(names3);
        }
    }   

4. 字符串拼接
#

+号拼接

$号格式化

using UnityEngine;

public class Hello : MonoBehaviour
{
    private void Start()
    {
        // +号拼接
        // 我是AA,我今年18岁

        string name = "AA";
        int age = 18;
        // int类型和字符串相加时,int会自动转换为字符串
        string info = "我是" + name + ",我今年" + age + "岁";
        Debug.Log(info);

        // $格式化输出,用{}去包裹变量
        // $格式化输出时,@符号会对整体进行取消转义
        string info2 = $"我是{name},我今年{age}岁";
        Debug.Log(info2);
    }
}   

5. 变量的类型转换
#

  1. 重新学习变量的定义方式

    • 定义但是没有赋值,这种情况下无法使用变量

    • 定义并且赋值

    • 同类型变量批量定义

      • 全部赋值

      • 部分赋值

      • 全部不赋值

        // 批量定义,只需要写一次类型,变量名用逗号分隔
        int num1 = 1, num2 = 2, num3 = 3;
  2. =式的理解

    • 等式右边的结果会赋值给左边,所以左边只能是变量,不能是数值
    • 等式两边类型一直,不能int a = “123”
    • 变量可以在等式两边任何一边
  3. 数值也有类型

    C#内置的类型背后都有一个实际类型,鼠标放在类型关键字或数值上都能看到

    • string: String
    • int: Int32
    • float: Single
int age = 25;
Int32 age2 = 30; // Int32 等于 int
  1. 类型的转换方式

    • 隐式类型转换:无需人为干涉的自动转换

      1. 类型上的兼容

      2. 精度小的转为精度大的

        int num = 10;
        float fnum = num; // 把num的数值转为float类型
    • 显式类型转换:人为干涉的强制转换

      1. 类型上的兼容

      2. 如果将精度大的转为精度小的,会丢失那些无法保存的精度

         // 显式转换,丢失精度
         float fnum3 = 10.01f;
         int num3 = (int)fnum3; // 将float转换为int,丢失小数部分
    • Convert强行转换(支持不兼容的类型)

      本身对放进去的变量类型没什么限制,但是如果完全不具备转换的可能性,会报错

      • 转换为Init: Convert.ToInt32

      • 转换为float: Convert.ToSingle

      • 转换为string: Convert.ToString

         Convert.ToInt32("123");
         Convert.ToSingle("1233.45");
         Convert.ToString(123);
  2. 字符串转换

     // ToString(),转为字符串
     int age = 19;
     string ageStr1 = age + ""; // 不推荐的做法
     string ageStr2 = age.ToString(); // 推荐的做法

6. 作用域和常量
#

作用域
#

C#中用大括号{}表示语句块、层次关系

而变量具备一个有效范围,这个范围就是他的作用域

  1. 变量只能在定义之后使用

  2. 变量只在它所在的“范围内有效”,这个范围可以简单的理解成大括号{}内

  3. 但是大括号{}有嵌套的情况时

    “父级”的变量可以用在“子级”中

    “子级”的变量不可以用在父级中

常量
#

  1. 常量是固定的值,程序运行时不会修改的值,比如圆周率π就不会因任何原因而修改

  2. 非常类似变量,但是它在定义时必须赋值,其他时刻无法改变

  3. 常量同样有作用域

     const float pi = 3.1415926f;

7. 算数运算符
#

运算符描述
+
-
*
/
%取余
++自增1
自减1

前++、后++的区别(–同理)

int a = 1;
int b = a++; // b = 1

int a = 1;
int b = ++a; // b = 2

前++是先自加再使用

而后++是先使用再自加

8. 赋值运算符
#

运算符描述
=等号
+=A+=B相当于A = A + B
-=A-=B相当于A = A - B
*=A*=B相当于A = A * B
/=A/=B相当于A = A / B
%=A%=B相当于A = A % B
int a = 3;
a += 4; // 7  等同于a = a + 4;
a -= 2; // 5  等同于a = a - 2;
a *= 2; // 10 等同于a = a * 2;
a /= 5; // 2  等同于a = a / 5;
a %= 3; // 2  等同于a = a % 3;

Debug.Log(a); // 输出结果为2

9. bool类型与关系运算符
#

bool:布尔类型一般用来描述“是与否”、“真与假”、“能与不能”、“正确与错误”等情况

有两个值:

  1. True:代表是
  2. False:代表否

10. 关系运算符
#

运算符描述
==检查两个值是否相等
!=检查两个值是否不相等
>左边是否大于右边
<左边是否小于右边
>=左边是否大于或等于右边
<=左边是否小于或等于右边

11. 逻辑运算符
#

运算符描述
&同时,左右都是true返回true,一个为false就返回false
|或,左右都是false返回false,一个为true就返回true
&&同时,左右都是true返回true,一个为false就返回false
||或,左右都是false返回false,一个为true就返回true
!取反,true转为false,false转为true

&:实际上是按位与,但是目前如果深究这块,会比较复杂,所以可以按照上面的理解

|:实际上是按位或,但是目前如果深究这块,会比较复杂,所以可以按照上面的理解

&&、||都会发生短路机制,也就是如果左边已经为false则不会执行右边,实际开发中使用较多

短路机制:一但确定了结论,后面就不执行,确定的顺序是从左往右(性能会更好)

 string userName = "User2";
 string password = "Password123";

 bool checkUserName = userName == "User";
 bool checkPassword = password == "Password123";

 bool online = true; // 当前网络在线

 bool canLogin = checkUserName & checkPassword & online; // 能登录 = 用户名正确 同时 密码正确
 // bool canLogin = (checkUserName & checkPassword) & online;
 Debug.Log($"玩家可以登录: {canLogin}");

 bool admin = true; // 是否是管理员
 bool adminCanLogin = checkUserName | admin; // 管理员可以登录 = 用户名正确 或者 是管理员
 Debug.Log($"管理员可以登录: {adminCanLogin}");

 Debug.Log(!false);
 Debug.Log(!true);
 // ! 是逻辑非运算符,用于取反布尔值。
 // 它会将 true 变为 false,将 false 变为 true。

// &  // 按位与运算符
// && // 逻辑与运算符
// |  // 按位或运算符
// || // 逻辑或运算符
// & 跟 && 的区别:
// & 是按位与运算符,会对每一位进行比较,只有当两个操作数的对应位都为1时,结果才为1。
// && 是逻辑与运算符,只有当两个操作数都为true时,结果才为true,并且如果第一个操作数为false,则不会计算第二个操作数(短路求值)。
// | 跟 || 的区别:
// | 是按位或运算符,会对每一位进行比较,只要有一个操作数的对应位为1,结果就为1。
// || 是逻辑或运算符,只要有一个操作数为true,结果就为true,并且如果第一个操作数为true,则不会计算第二个操作数(短路求值)。

12. 三目运算符
#

也被称为条件运算符

三目运算符并不是一种必要的运算符,是为了方便

结果 = 如果条件为真 ?则为X :否则为Y

C# 三元运算符的语法示例

int value = -80;
int num = value < 0 ? 0 : value; // 如果num小于0,则num赋值为0,否则不变
Debug.Log(num);

// 最多是100
int num2 = value > 100 ? 100 : value; // 如果num大于100,则num2赋值为100,否则num2赋值为num
Debug.Log(num2);

// 控制在0到100之间
int num3 = value < 0 ? 0 : value;
num3 = num3 > 100 ? 100 : num3;
Debug.Log(num3);

13. if判断语句
#

if(如果):用来判断条件是否成立,即是否为true

分支语句:不能单独使用

else if (其他可能性):if不成立,但是满足我的条件,则执行。

else:以上条件都不满足则执行

注意事项
#

  1. 短路机制,if如果执行成功则不会执行else if 或 else 的语句,短路机制不只是不执行{}中的代码,判断条件本身也不会运行(因为判断条件有时候本身是有计算过程的)
  2. if语句中大括号也存在变量的作用域问题

14. 枚举类型
#

特别适合用来描述状态、阶段

当某个数据可以预期有确定性的几种结构时,我们可以使用枚举类型来保存,比如

  1. AI:待机、攻击、死亡

  2. 时间:周一、周二、周三

  3. 游戏状态:选择英雄、加载界面、战斗中、游戏结束

枚举是一种由我们自己去创建的类型,也是我们首次去创建我们自己的类型

  1. 定义枚举类型,一般我们会将它定义在class同级中或单独新建一个脚本文件去保存,不要定义在函数中

  2. 定义这个枚举类型的变量

  3. 使用这个变量

     /// <summary>
    /// AI状态
    /// </summary>
    enum AIState
    {
        /// <summary>
        /// 待机
        /// </summary>
        Idle,
        /// <summary>
        /// 攻击
        /// </summary>
        Attack,
        /// <summary>
        /// 死亡
        /// </summary>
        Die = 1000 // 自定义状态对应的值
    }

15. Switch语句
#

switch语句和if else if 非常接近,甚至理论上不存在也没有任何关系,不过是特定情况下会更加方便

语法:
#

C# switch 语句的基础语法

对比if语句
#

if else if的特点是判断的因素可以完全不相关,只要条件都是bool类型即可,比如:

if(3==3)
{

}
else if (a2 > a3)
{

}

但是Switch一般用在围绕一个因素去做判断,比如当前是星期几,AI处于什么状态,所以和枚举类型特别搭配

break
#

并不是每一个case都需要break,如果case 语句后面没有要执行的代码,则可以不包含 break 关键字, 这时程序会继续执行后续的case语句,直至遇到break关键字为止;

这种情况的目的一般在于,如果我们希望当AI状态为巡逻、待机时候要做的事情是一样的

多个 case 共用同一段逻辑的 switch 示例

16. for循环
#

一般用于循环固定次数的情况

语法
#

C# for 循环的基础语法

运行步骤:
#

  1. 初始化代码,只会执行一次,也可以不写

  2. 判断条件是否满足

    1. 满足则执行{}中的主代码片段

      • 执行更新时间
      • 判断条件,满足则继续执行…(后续循环)
    2. 不满足则退出循环

// 案例:1+2+3+4+5....+100
int res = 0;
for (int i = 1; i <= 100; i++)
{
    res += i; // 将i的值累加到res中
    Debug.Log($"i = {i}, res = {res}");

}
Debug.Log($"1+2+3+4+5....+100 = {res}");

17. while循环语句
#

基于条件的循环,也就是条件满足就一直循环

// 避免死循环
// 从1加到100
int num = 1;
int res = 0;
while (num<=100)
{
    res += num;
    num++;
    Debug.Log($"当前num={num}, res={res}");
}
Debug.Log(res);

还有一个变种 do while,但是用的少

// 至少执行一次
do
{
    // 首先执行一次
    // 循环体
    Debug.Log("至少执行一次");
} while (false); // 如果条件满足才会执行下一次
C# 学习加固 - 这篇文章属于一个选集。
§ 1: 本文