C#编程流程控制与集合类型(一)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: C#编程流程控制与集合类型

本文主要来自<<C#实践入门>>哈里森.费隆 著,仅用为做笔记。

本章将专注以下主题:

  • 选择语句。
  • 使用数组(Array)、字典(Dictionary)和列表(List)。
  • 使用for、foreach和do-while 循环进行选代。
  • 使用break、continue和return 关键字执行控制
  • 理解C#枚举(Enumeration)。

选择语句


最复杂的编程问题通常可以归结为对游戏或程序进行评估并执行的一系列简单选择。。因为 Visual Studio和Unity 无法自己做出这些选择,所以这些决定要由我们来做。通过使用ifelse和 switch 选择语句,我们可以基于一个或多个条件以及每种情况下将要执行的操作来指定分支路径。

传统上,这些条件包括:

  • 检测用户输入。
  • 计算表达式和布尔逻辑
  • 比较变量或字面值。

if-else语句


使用if-else 语句是在代码中进行决策的最常见方式。抛开语法不讲,if-else的基本思想是:如果条件满足,就执行一段代码;如果不满足,就执行另一段代可以将这些语句视为以条件为钥匙的门。为了顺利通过,条件必须是有效的:否绝通过,代码将被发送到下一个可能的门。

1.基本语法

有效的if-else 语句需要具备以下条件:

  • 行首的if关键字。
  • 用于存储条件的一对括号。
  • 用于存储代码块的一对花括号。
  • 带有自己的花括号和代码块的 else 关键字(可选)

语法如下:

if(condition is true)
{ 
  Execute this code block
}
else
{
  Execute this code block
}

因为这些都是对逻辑思维的很好介绍,至少在编程中是这样,所以我们将更详细地分析三种不同的ifelse 变体。


1.如果不关心条件没有满足时会发生什么,那么只使用 if语句即可。在图41中如果hasDungeonKey为tue,就输出调试日志;如果为 false,则不执行任何代码。

 public class LearningCurve : MonoBehaviour
{
 public bool hasDungeonKey = true;
 // Use this for initialization
 void Start()
 {
    if(hasDungeonKey)
    {
     Debug.Log("You possess the sacred key - enter.");
    }
 }
}

2.在无论条件满足与否都需要采取措施的情况下,可以添加 else 语句。如果hasDungeonKey为false,f语句将失败,并且控制流将跳至else 语句,如图42所示

public class LearningCurve : MonoBehaviour
{
 public bool hasDungeonKey = true;
 // Use this for initialization
 void Start()
{
 if(hasDungeonKey)
  {
     Debug.Log("You possess the sacred key - enter.");
  }
 else
  {
     Debug.Log("You have not proved yourself worthy,warrion"):
  }
}
}

3. 对于需要两个以上可能结果的情况,可以添加带有括号、条件和花括号的else-if语句。这会是最好的展示而不是解释,我们将在后面介绍。

实践一一小偷的预期

下面编写一条 ifelse 语句,用于检查口袋中的金额,并针对三种不同的情况返回不同的调试日志。

(1)打开 LearningCurve 脚本并添加一个名为 currentGold 的int变量,将值设置为一个介于1和100之间的整数。


(2)添加f语句来检查 currentGold 是否大于50。如果大于,就向控制台打印一条消息。


(3)添加else-if语句来检查 currentGold 是否小于15。如果小于,就打印另一条不同的调试日志。


(4)添加不带任何条件的else 语句和想要打印的默认日志。


(5)如图4-3 所示保存文件并单击 Play 按钮。

 public class LearningCurve:MonoBehaviour
{
 public Int currentGold = 32;
// Use this for initialization
void Start()
{
  if(currentGold >50)
   {
     Debug.Log("You're rolling in it - beware of pickpockets.");
   }
  else if(currentGold < 15)
  {
     Debug.Log("Not much there to steal.");
  }
  else
   {
     Debug.Log("Looks like your purse is in the sweet spot.“);
   }
}
}

这里将currenGold 设置为 32,我们可以按以下方式分解代码。


因为curenrGold 不大于50,所以跳过if语句及其调试日志。

因为currentGold 不小于15,所以也跳过else-if语句及其调试日志

由于之前的条件都不满足,因此执行 else 语句并显示默认日志。

image.png

2.使用逻辑非运算符

用例并不总是需要检查条件为 tue 的情况,而这正是逻辑非运算符出现的原因。逻辑非运算符允许我们检查迁或 else-if 语句满足条件为false 时的情况。如前所述,我们可以在 f条件下检查布尔值、字面值或表达式,所以很自然地,逻辑非运算符必须具有适应性。

public class LearningCurve : MonoBehaviour
{
 public bool hasDungeonKey = false;
 public string weaponType =“Arcane Staff";
// Use this for initialization
void Start()
{
 if(!hasDungeonKey)
 {
  Debug.Log("You may not enter without the sacred keyl.");
 }
if(weaponType !="Longsword")
 {
  Debug.Log("You don't appear to have the right type of weapon...");
  }
}
}
  • 第一条if语句可以解释为:如果hasDungeonKey 为false,那么if语句的值为true,执行其代码块
  • 第二条if语句可以解释为:如果 weaponType 的值不等于字符串"Longsword"那么执行其代码块。
  • 看到调试结果,如果仍然不理解,那么可以复制前面的代码到LearningCurve 脚本中,运行这些变量,直到它们奏效。

3.嵌套语句

ifelse 语句最有价值的功能之一是它们可以相互嵌套,从而创建复杂的逻辑路线在编程中,我们称之为决策树。就像真实的走廊一样,其他门的后面也可以有门,像是迷宫

首先观察图下的示例:

  • 首先,第一条 if 语句检查 weaponEquipped 是否为 tue。这里只关心weaponEquipped 是否为true,而不管 weaponType 是什么。
  • 接下来,第二条if语句检查 weaponType 并打印相关的调试日志
  • 如果第一条if语句为 false,那么控制流转到 else 语句并打印相关的调试日志
  • 如果第二条if 语句为 false,那么因为没有对应的 else 语句,因而不会打印任何内容。
public elass LearningCurve : MonoBehaviour
{
  public bool weaponEquipped = true;
  public string weaponType ="Longsword";
  void Start()
  {
    if(weaponEquipped)
    {
     if(weaponType==“Longsword")
      {
       Debug.Log("For the Queen!");
      }
     }
     else
     {
       Debug.Log("Fists aren't going to work against armor...");
     }
   }
}

4.计算多个条件

除了套语句,还可以使用 AND 和 OR 逻辑运算符将多个条件检查组合到单个if或elseif语句中:

  • AND逻辑运算符使用&&表示。使用AND 逻辑运算符意味着只有当i语句的所有条件都为tue时,if语句的值才为true。
  • OR 逻辑运算符使用|表示。使用 OR 逻辑算符意味着只要语句有一个条件为true,if语句的值就为true。

if语句已更新为检查 weaponEquipped和weaponType,只有当它们两者都为true时才执行代码块。

if(weaponEquipped  weapanType =="Longsword”)
{
 Debug.Log("For the queen!“);
}

实践一到达宝藏

下面进一步巩固所学知识。

(1)在 LearningCurve 脚本的顶部声明三个变量:pureOfHeart 是布尔值,应该true;hasSecretIncantation 还是布尔值,应该为false; rareltem 是字符串,值取决于你.

(2)创建一个名为OpenTreasureChamber 的没有返回值的 public 方法,并在 Start方法中调用。


(3)在OpenTreasureChamber 方法中声明 ifelse 语句来检查 pureOfHeart 是否为

true,并检查rareltem 是否和赋予的字符串匹配。


(4)在第一条f语句中创建套的 if-else 语句,检查hasSecretIncantation 是否为false。


(5)为每个if-else语句添加调试日志,保存后单击Play 按钮。

刚刚发生了什么

将会打印套的if 语句的调试日志,如图这意味着代码通过第一条 if 语句中的两个条件,但第二条if 语句中的条件没有通过

switch语句


嵌套过多的代码最终将难以理解,且很难修改。switch 语句能让我们为每种可能的结果编写代码,但格式相比 if-else 语句更为简洁。

1.基本语法

switch语句需要具备以下条件。

  • switch 关键字,后跟随一对用来放置条件的括号
  • 一对花括号。
  • 以冒号结尾的每种可能情况的 case 子句:单独的代码行或方法,后跟 break 关键字和分号
  • 默认的 default 子句以冒号结尾:单独的代码行或方法,后跟 break 关键字和分号

以蓝图的形式看起来语法如下:

switch (matohExpression)
{
 case matchValvel:
  Executing code block
  break;
 case matchValue2 :
  Executing code block
  break;
 default:
  Executing code block
  break;
}

在case子句中,冒号和break 关键字之间的任何内容都类似于ifelse语句中的代码块。break关键字用于告诉程序在选定的条件触发后完全退出switch语句。

2模式匹配

在switch语句中,模式匹配是 case 子句用来验证匹配表达式的方式。匹配表达式可以是不为null的任何类型。所有 case 子句的值都要对应匹配表达式的类型。例如,对于计算整型数的 switch 语句,每个 case 子句都需要指定一个整数来进行检查。与匹配表达式匹配的case 子句会被执行。如果没有匹配的case 子句,则执行 defaut 子句.

实践一选择动作

这里包含大量新的语法和信息,但 switch 语句有助于观察实际操作。下面让我们为游戏角色可以采取的不同操作创建一条简单的 switch 语句。

(1)创建一个名为 characterAction 的字符串类型的成员变量或局部变量,设置为"Attack"。


(2)声明一条 switch 语句,使用 characterAction 作为匹配表达式。


(3)创建两个 case 子句以打印不同的调试日志,并且不要忘记在每个 case 子句的末尾添加 break 关键字。


(4)添加带有调试日志的 default 子句和 break 关键字。

(5)保存脚本并在Unity 单击 Play 按钮,

 public class LearningCurve : MonoBehaviour
{
  void Start()
  {
   string characterAction =“Attack";
   switch(characterAction)
   {
     case"Heal":
       Debug.Log("Potion sent,");
       break;
     case"Attack":
       Debug.Log("To arms!");
       break;
     default:
       Debug.Log("Shields up.");
       break;
   }
  }
}

刚刚发生了什么

由于 characterAction 被设置为"Attack”,因此 switch 语句执行第二个 case 子句并打印相应的调试日志,更改 characterAction 为"Hea"或其他尚未定义的动作,以看到第一个 case 子句和 default 子句的执行情况。

image.png

3.fall-through

switch 语句能够在多种情况下执行相同的操作,类似于我们在一条f语句中指定多个条件。这种情况被称为fall-through。如果 case 子句没有代码块和 break 关键字那么控制流将直接进入下一个 case 子句。

实践一掷骰子

下面使用switch语和fall-through来模拟掷子游戏.

(1)创建一个名为diceRoll 的iint 变量,赋值为7


(2)声明switch语句并使用diceRoll作为匹配表达式


(3)为掷散添加3种可能的情况。


(4)当匹配表达式为15和20时,有相应的调试日志和break 关键字,为7时则直接进入掷散结果为15的case 子句。


(5)保存脚本并在Unity单击Play 按钮,

void Start()
{
   int diceRoll=7;
   switch(diceRoll)
   { 
    case 7:
    case 15:
      Debug.Log("Mediocre damage,not bad");
      break;
    case 20:
     Debug.Log("Critical hit,the creature goes down!");
      break;
     default: 
      Debug.Log("you comletely missed and fell on your face.");
      break;
    }
}

刚刚发生了什么

当把diceRoll设置为7时,switch 语句会匹配第一个case子句,但由于其中没有代码块和 break 关键字,因而进入并执行下一个case子句中的代码块,如图如果将 diceRoll 更改为15或20,那么控制台将显示对应的调试日志,对于其他任何值,则直接进入switch 语句末尾的 default 子句。

集合一览


到目前为止,我们只需要使用变量来存储单个值,但很多情况下我们需要存储一组值。此时,集合就派上用场了。C#中的集合类型包括数组(Array)、字典(Dictionary)和列表(List),它们各有优缺点。

数组


数组是 C#提供的最基本的集合。可以将数组视为一组值的容器,这些值在编程术语中被称为元素,每个值都可以单独访问或修改。

  • 数组可以存储任何类型的值,但其中的所有元素都必须是同一类型.
  • 数组的长度或元素个数是在创建时设置的,且之后不能再修改。
  • 如果在创建时没有分配初始值的话,那么每个元素会使用默认值。存储数字类型的数组的默认值为0,而存储其他类型数据的数组的默认值为 null。
  • 数组是 C#中最不灵活的集合类型,主要是因为元素在创建后不能添加或删除。不过,当需要存储不太可能改变的信息时,数组特别有用

1.基本语法

声明数组类似于声明我们之前使用的其他变量类型,但也有一些差别。

  • 数组需要指定的元素类型、一对方括号和唯一的名称。
  • new 关键字用于在内存中创建数组,后跟值的类型和另一对方括号
  • 数组将要存储的元素数量则放在第二对方括号中

以蓝图的形式看起来语法如下:

elementType[] name = new elementType[numberOfElementsl;

举个例子,假设我们需要在游戏中存储得分的前三名:

int[] topPlayerScores = new int[3];

topPlayerScores 是存储了3个整型元素的整型数组,由于我们没有添加任何初始值,因此topPlayerScores 数组中的3个元素默认都为0

C#为此提供了两种有效的语法:普通语法和速记语法

// Longhand initializer
int[] topPlayerScores = new int[] (713,549,984);
// Shortcut initializer
int[] topPlayerScores ={ 713,549,984 };

2.索引和下标

每个数组元素都按分配的顺序进行存储,这称为索引。数组元素从0开始索引,这意味着数组元素的顺序从0而不是1开始。可以将数组元素的索引看作引用或位置在 topPlayerScores 数组中,第1个整数452的索引为0,第2个整数713 的索引为1,第3个整数984的索引为2。

使用下标运算符还可以直接修改数组中的值,就像其他变量一样,甚至可以将自己作为表达式来传递:

topPlayerScores[1] = 1001;
Debug.Log(topPlayerScores[1]);

topPlayerScores 数组中的值现在是452、1001和984。

3. 范围异常

在创建数组时,元素的数量是固定的,这意味着我们不能访问不存在的元素。对于topPlayerScores数组来说,数组长度为3,因此有效索引的范围是0-2。任何大于或等于 3的索引都将超出数组的索引范围,并在控制台中生成名为IndexOutOfRangeException 的调试日志。

image.png


相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
1月前
|
开发框架 前端开发 .NET
C#编程与Web开发
【4月更文挑战第21天】本文探讨了C#在Web开发中的应用,包括使用ASP.NET框架、MVC模式、Web API和Entity Framework。C#作为.NET框架的主要语言,结合这些工具,能创建动态、高效的Web应用。实际案例涉及企业级应用、电子商务和社交媒体平台。尽管面临竞争和挑战,但C#在Web开发领域的前景将持续拓展。
|
2天前
|
存储 C# 开发者
C# 编程基础:注释、变量、常量、数据类型和自定义类型
C# 编程基础:注释、变量、常量、数据类型和自定义类型
7 1
|
1月前
|
安全 API C#
C#.Net筑基-类型系统②常见类型--枚举Enum
枚举(enum)是C#中的一种值类型,用于创建一组命名的整数常量。它们基于整数类型(如int、byte等),默认为int。枚举成员可指定值,未指定则从0开始自动递增。默认值为0。枚举可以与整数类型互相转换,并可通过`[Flags]`特性表示位域,支持位操作,用于多选场景。`System.Enum`类提供了如`HasFlag`、`GetName`等方法进行枚举操作。
|
1月前
|
编译器 C#
C#.Net筑基-类型系统②常见类型 --record是什么类型?
`record`在C#中是一种创建简单、只读数据结构的方式,常用于轻量级数据传输。它本质上是类(默认)或结构体的快捷形式,包含自动生成的属性、`Equals`、`ToString`、解构赋值等方法。记录类型可以继承其他record或接口,但不继承普通类。支持使用`with`语句创建副本。例如,`public record User(string Name, int Age)`会被编译为包含属性、相等比较和`ToString()`等方法的类。记录类型提供了解构赋值和自定义实现,如密封的`sealed`记录,防止子类重写。
|
1月前
|
存储 C#
C#.Net筑基-类型系统②常见类型--结构体类型Struct
本文介绍了C#中的结构体(struct)是一种用户自定义的值类型,适用于定义简单数据结构。结构体可以有构造函数,能定义字段、属性和方法,但不能有终结器或继承其他类。它们在栈上分配,参数传递为值传递,但在类成员或包含引用类型字段时例外。文章还提到了`readonly struct`和`ref struct`,前者要求所有字段为只读,后者强制结构体存储在栈上,适用于高性能场景,如Span和ReadOnlySpan。
|
1月前
|
存储 安全 Unix
C#.Net筑基-类型系统②常见类型--日期和时间的故事
在System命名空间中,有几种表示日期时间的不可变结构体(Struct):DateTime、DateTimeOffset、TimeSpan、DateOnly和TimeOnly。DateTime包含当前本地或UTC时间,以及最小和最大值;DateTimeOffset增加了时区偏移信息,适合跨时区操作。UTC是世界标准时间,而格林尼治标准时间(GMT)不稳定,已被更精确的UTC取代。DateTimeOffset和DateTime提供了转换为UTC和本地时间的方法,以及各种解析和格式化函数。
|
1月前
|
存储 安全 网络安全
C#编程的安全性与加密技术
【4月更文挑战第21天】C#在.NET框架支持下,以其面向对象和高级特性成为安全软件开发的利器。本文探讨C#在安全加密领域的应用,包括使用System.Security.Cryptography库实现加密算法,利用SSL/TLS保障网络传输安全,进行身份验证,并强调编写安全代码的重要性。实际案例涵盖在线支付、企业应用和文件加密,展示了C#在应对安全挑战的同时,不断拓展其在该领域的潜力和未来前景。
|
1月前
|
SQL 开发框架 安全
C#编程与多线程处理
【4月更文挑战第21天】探索C#多线程处理,提升程序性能与响应性。了解C#中的Thread、Task类及Async/Await关键字,掌握线程同步与安全,实践并发计算、网络服务及UI优化。跟随未来发展趋势,利用C#打造高效应用。
|
1月前
|
开发框架 .NET Java
探索 C#编程的奥秘与魅力
【4月更文挑战第20天】C#是微软开发的现代、面向对象的编程语言,以其简洁语法、强大功能和跨平台支持脱颖而出。它支持自动垃圾回收、泛型、委托、LINQ,并广泛应用于桌面、Web、移动和游戏开发。C#拥有活跃的开发者社区和丰富的资源,是Unity游戏开发的首选语言。随着.NET Core,C#可在多个操作系统上运行,持续创新,未来发展潜力巨大。
|
1月前
|
人工智能 C# 云计算
C#编程的未来发展趋向
【4月更文挑战第21天】C#编程未来将深化跨平台支持,强化云计算与容器技术集成,如.NET Core、Docker。在AI和ML领域,C#将提供更丰富框架,与AI芯片集成。语言和工具将持续创新,优化异步编程,如Task、async和await,提升多核性能。开源生态的壮大将吸引更多开发者,共创更多机遇。