Skip to main content

数据字典

数据字典通过键值对存储数据令牌,类似于 C# Dictionary。大多数数据字典函数只是底层 C# 字典的包装器,因此如果您需要更具体的细节,C# 字典文档也同样适用。

数据字典的键和值都是数据令牌。这意味着您可以有效地使用任何内容作为键。但是,如果要序列化为 VRCJSON,则仅支持字符串键。

如果您使用 UdonSharp,请包含 using VRC.SDK3.Data; 指令以使用数据字典。

构造函数​

构造函数结果
DataDictionary()构造一个具有默认初始容量的空 DataDictionary。
DataDictionary(int)构造一个具有指定初始容量的空 DataDictionary。详情请参见 C# 文档。

属性​

属性结果
Count获取字典中的元素数量

函数​

函数输入输出结果
AddDataToken key, DataToken value在指定键处添加值。此函数与 SetValue 的区别在于,如果键已存在,将抛出异常。这对于初始化很有用,因为它会导致编译错误,但不建议用于正常使用,因为它可能导致运行时错误并停止你的 UdonBehaviour。
Clear从此字典中移除所有键和值
ContainsKeyDataToken keybool result如果此字典中存在指定键,则返回 true。
ContainsValueDataToken keybool result如果此字典中存在指定值,则返回 true。
DeepCloneDataDictionary result将 DataDictionary 克隆到包含所有相同值的新 DataDictionary 中。与 ShallowClone 不同,深克隆意味着它将递归导航到每个 DataList 或 DataDictionary 内部,并复制其内容。TokenType 为"Reference"的项将保持与原始项相同的引用,不会被深克隆,其中包括数组。
GetKeysDataList keys返回此 Data Dictionary 中所有键的 Data List。用于在 for 循环中迭代 Data Dictionary 中的所有项。
GetValuesDataList values返回此 Data Dictionary 中所有值的 Data List。
RemoveDataToken keybool success从此字典中移除指定键。如果成功移除了任何内容,则返回 true。
RemoveDataToken keybool success, DataToken value从此字典中移除指定键。如果成功移除了任何内容,则返回 true。如果成功,将移除的值复制到 out DataToken 中。
SetValueDataToken key, DataToken value在指定键处设置值。如果该键不存在,将添加一个新键。
ShallowCloneDataDictionary result将 DataDictionary 克隆到包含所有相同值的新 DataDictionary 中。与 DeepClone 不同,这意味着如果 DataDictionary 包含其他 DataList 和 DataDictionary,它们仍将是相同的引用。
TryGetValueDataToken keybool success, DataToken output从指定键获取令牌并将其放入 out DataToken 中。如果检索成功则返回 true,否则返回 false。检索失败时,将在 out DataToken 中放入 DataError 而不是结果。
TryGetValueDataToken key, TokenType expectedbool success, DataToken output从指定键获取令牌并将其放入 out DataToken 中。如果检索成功则返回 true,否则返回 false。检索失败时,将在 out DataToken 中放入 DataError 而不是结果。此版本的 TryGetValue 包含 TokenType,这意味着它将自动进行类型检查。如果类型不匹配,将返回 false 并带有 DataError.TypeMismatch

请注意,在从 JSON 生成的 Data Dictionary 上调用影响或查看所有值的函数,如 ContainsValue、ShallowClone 和 GetValues,将解析所有尚未解析的顶层值,如果值很多,这可能很昂贵。解析后,将来的操作会更便宜。

从 DataDictionary 获取值​

有几种不同的方法可以从字典中获取值。每种方法都有自己的用例,由你选择使用哪种方法。

TryGetValue​

如果你想安全地从字典中获取值,但不关心该位置存在什么类型,建议使用 TryGetValue。此函数根据获取值是否成功返回 true 或 false。它旨在放在 if 或 branch 的条件中,以便清楚地知道成功时和失败时发生的情况。

TryGetValue 示例
if (dictionary.TryGetValue("key", out DataToken value)) {
Debug.Log($"成功!{value}");
} else {
Debug.Log($"失败!{value}");
}

如果失败,你收到的 DataToken 仍然有效,但包含的不是你的数据,而是一个错误。

当你想要从某个位置获取某个值但不关心具体是什么时,此方法很好。

由于此函数没有内置类型检查,你应该将其与某种类型检查配对使用,无论是 if、branch 还是 switch。如果你只关心一种特定类型,则建议使用带 TokenType 的 TryGetValue 版本,它会自动进行类型检查。

带 TokenType 的 TryGetValue​

如果你想从字典中获取值但不知道它可能是什么类型,进行类型检查很重要。你可以自己在代码中执行此操作,但这可能会变得混乱。相反,你可以使用包含 TokenType 的 TryGetValue 版本。这样做时,它指示你仅当值是你期望的类型时才检索该值。否则,它返回 false,并且可以优雅地处理。

此方法适用于你想从特定位置获取特定值,但数据来自外部来源,因此你不确定来源是否有正确的数据。

带 TokenType 的 TryGetValue 示例
// 你可以这样做,但有点丑陋
if (dictionary.TryGetValue("key", out DataToken value)) {
if (value.TokenType == TokenType.DataDictionary)
{
Debug.Log($"成功!匹配的字典有 {value.DataDictionary.Count} 个项目");
}
}

// 这种方法内置了类型检查!功能相同,但更简洁。
if (dictionary.TryGetValue("key", TokenType.DataDictionary, out value)) {
Debug.Log($"成功!匹配的字典有 {value.DataDictionary.Count} 个项目");
}

简写括号语法​

你也可以使用括号语法设置和获取数据字典中的项目,例如在 UdonSharp 中使用 dictionary["key"] = "value"; 或在 Udon Graph 中使用 DataDictionary Get Item 节点。此方法更小且更易于使用。但请注意,这并非完全安全,如果你尝试执行无效操作(例如从不存在的键获取值),可能会导致 udonbehaviour 停止。

此方法适用于你完全控制数据、可以保证数据存在并且是你期望的类型时。否则,建议使用某种形式的 TryGetValue。

简写括号语法示例
dictionary["A"] = 5;
dictionary["B"] = 10;

// 这假设 A 和 B 将始终包含整数。
// 这是一个安全的假设,因为我们刚刚在受控环境中设置了它们。
// 如果数据来自外部来源,我们不应该做这些假设!
int sum = dictionary["A"].Int + dictionary["B"].Int;

初始化数据字典​

在 UdonSharp 中,数据字典可以在私有变量中初始化。这允许你拥有在代码运行之前定义的预先存在的数据集。这也支持嵌套字典和 DataTokens 支持的任何其他内容。以下是应如何使用此语法的示例:

初始化数据字典示例
private DataDictionary users = new DataDictionary()
{
{ "John Doe", new DataDictionary()
{
{"email", "johndoe@example.com"},
{"age", 35},
{"address", new DataDictionary()
{
{"street", "123 Main St"},
{"city", "Anytown"},
{"state", "CA"},
{"zip", 12345}
}
}
}
},
{ "Jane Smith", new DataDictionary()
{
{"email", "janesmith@example.com"},
{"age", 28},
{"address", new DataDictionary()
{
{"street", "456 Elm St"},
{"city", "Anytown"},
{"state", "CA"},
{"zip", 12345}
}
}
}
},
{ "Bob Johnson", new DataDictionary()
{
{"email", "bobjohnson@example.com"},
{"age", 42},
{"address", new DataDictionary()
{
{"street", "789 Oak St"},
{"city", "Anytown"},
{"state", "CA"},
{"zip", 12345}
}
}
}
}
};

目前,UdonSharp 不支持函数内部的此类初始化器。这将是 UdonSharp 的功能请求。

目前,Unity 不会序列化 DataDictionary,这意味着不建议将其用于序列化的公共变量。 应仅用于 private 或 [NonSerialized] public 变量。这是我们仍在努力开发的功能的补充。

迭代字典中的所有条目​

迭代字典中的所有条目与列表略有不同,因为字典是无序的。你不能直接索引字典,必须使用键。为此,我们有 GetKeys() 函数。此函数返回字典中所有键的 DataList。获得该列表后,你可以使用 for 循环遍历所有键并访问每个键处的值。

迭代字典中所有条目的示例
// 首先获取字典中的所有键
DataList keys = dictionary.GetKeys();

// For 循环遍历所有键
for (int i = 0; i < keys.Count; i++)
{
// 获取当前索引处的键
DataToken key = keys[i];

// 访问该键对应的条目
Debug.Log(dictionary[key].ToString());
}

GetKeys 乍看之下可能很昂贵,但只要不添加或移除键,它就会被缓存,因此除了 Udon 本身的开销外,频繁访问的性能通常是良好的。

如果你需要以排序方式迭代字典,利用此方法的一个巧妙技巧是:先 GetKeys(),然后 Sort() 键,再进行 for 循环。字典本身没有任何排序概念且无法排序,但你可以排序用于访问它的列表!

如果你想将字典的值与键分开处理,也可以使用 GetValues()。这对于某些你需要明确列出所有值的应用很有用,但值得一提的是,如果你不熟悉字典,此方法可能具有欺骗性。原因之一是字典没有顺序,因此你永远不应依赖特定项在通过 GetValues() 获取时始终位于特定索引,也不应期望这些项的索引与 GetKeys() 中找到的索引正确匹配。在大多数情况下,GetKeys() 就是你所需要的,而 GetValues() 更多是为需要对字典进行更高级控制的人提供的选项。

与其他玩家通过网络同步数据字典​

数据字典不能直接同步。但可以使用 VRCJson 序列化为 JSON 字符串或从 JSON 字符串反序列化。这是当前推荐的与 UdonSync 同步数据字典的方法。

一种方法是使用 OnPreSerialization 和 OnDeserialization 来序列化和反序列化 json 字符串。使用此方法,你无需担心代码其余部分的序列化,只需设置值即可。

与其他玩家通过网络同步数据字典的示例
[UdonSynced]
private string _json = "";
private DataDictionary _dictionary;

public override void OnPreSerialization()
{
if (VRCJson.TrySerializeToJson(_dictionary, JsonExportType.Minify, out DataToken result))
{
_json = result.String;
}
else
{
Debug.LogError(result.ToString());
}
}

public override void OnDeserialization()
{
if(VRCJson.TryDeserializeFromJson(_json, out DataToken result))
{
_dictionary = result.DataDictionary;
}
else
{
Debug.LogError(result.ToString());
}
}