DYNAMIC EXPLAINER / ARTICLE FLOW
01 / 10
把章节变成可单步观察的过程
分类:CSharp 基础数据结构
先说结论
var、泛型、out、lambda 和 foreach 不是五套孤立语法,它们经常共同出现在一次集合查询中:泛型约束元素类型,lambda 描述条件,out 返回额外结果,foreach 顺序消费集合,var 让局部类型由右侧表达式推导。
看一条完整数据流
敌人字典:{ 42 -> HP 20, 77 -> HP 80 }
TryGetValue(42, out enemy) -> true,enemy.HP = 20
lambda:enemy => enemy.HP < 30 -> true
结果:42 号敌人进入低血量集合
这里 out enemy 不是创建另一个敌人,lambda 也不会自动执行;它们只有在对应方法被调用时才参与这条数据流。
这些语法为什么重要
List<T>、Dictionary<TKey, TValue>、HashSet<T> 这些集合经常会和一些 C# 语法一起出现:
var
泛型 <T>
out
lambda =>
foreach
这些不是数据结构本身,但看集合代码时会反复遇到。
var
var 不是“没有类型”,而是让编译器自动推断类型。
var name = "Alice";
var score = 90;
var names = new List<string>();
等价于:
string name = "Alice";
int score = 90;
List<string> names = new List<string>();
一旦推断完成,类型就是固定的:
var score = 90;
score = "hello"; // 错,score 已经是 int
泛型 <T>
<T> 表示“这个集合里存什么类型”。
List<int> nums = new();
List<string> names = new();
Queue<GameObject> effects = new();
Dictionary<string, int> 有两个类型:
string:key 的类型
int:value 的类型
Dictionary<string, int> scores = new();
scores["Alice"] = 90;
含义:
用 string 当 key,找到 int 分数。
out
out 常见于 TryGetValue:
if (scores.TryGetValue("Alice", out int score))
{
Debug.Log(score);
}
out int score 的意思是:
给方法一个变量位置;
如果方法找到了结果,就把结果填到 score 里面。
可以拆开理解:
int score;
bool found = scores.TryGetValue("Alice", out score);
if (found)
{
Debug.Log(score);
}
这种 TryXxx(out value) 模式很常见:
int.TryParse("123", out int number);
float.TryParse("3.14", out float value);
dict.TryGetValue(key, out var item);
它的特点是:
不靠异常处理失败;
而是返回 true / false。
lambda =>
lambda 是临时写在调用处的小函数。
例如:
names.RemoveAll(name => name == "Alice");
name => name == "Alice" 的意思是:
给我一个 name;
我判断它是不是 Alice;
是就返回 true,不是就返回 false。
等价理解:
bool IsAlice(string name)
{
return name == "Alice";
}
常见写法:
names.RemoveAll(name => name.StartsWith("A"));
items.Find(item => item.Id == 1001);
items.Exists(item => item.Count > 0);
items.RemoveAll(item => item.IsExpired);
foreach
遍历集合:
foreach (string name in names)
{
Debug.Log(name);
}
含义:
从 names 里一个一个拿出 string,临时叫 name。
遍历字典:
foreach (var pair in scores)
{
Debug.Log($"{pair.Key}: {pair.Value}");
}
pair 是 KeyValuePair<string, int>。
完整写法:
foreach (KeyValuePair<string, int> pair in scores)
{
Debug.Log(pair.Key);
Debug.Log(pair.Value);
}
foreach 里不要修改集合
不要这样写:
foreach (var name in names)
{
if (name == "Alice")
{
names.Remove(name); // 危险
}
}
通常会报:
Collection was modified
要删除多个元素,用:
names.RemoveAll(name => name == "Alice");
或者倒序 for:
for (int i = names.Count - 1; i >= 0; i--)
{
if (names[i] == "Alice")
{
names.RemoveAt(i);
}
}
组合例子
Dictionary<string, List<int>> skillsByUnit = new();
if (!skillsByUnit.TryGetValue("monster_01", out var skills))
{
skills = new List<int>();
skillsByUnit["monster_01"] = skills;
}
skills.Add(1001);
skills.RemoveAll(skillId => skillId <= 0);
foreach (var skillId in skills)
{
Debug.Log(skillId);
}
这里同时用了:
Dictionary<string, List<int>>:泛型
new():目标类型推断
out var skills:从字典取值
lambda:RemoveAll 条件
foreach:遍历列表
最重要的收获
var:让编译器推断类型。
<T>:告诉集合里面存什么类型。
out:让方法把结果填出来。
=>:临时写一个小判断/小函数。
foreach:逐个遍历集合。
这些语法熟了之后,看集合代码会顺很多。