Skip to main content

数据令牌

数据令牌存储数据。每个令牌仅存储一个变量。数据令牌用于数据字典和数据列表。

数据令牌可以包含以下令牌类型:

  • Null
  • Boolean
  • SByte
  • Byte
  • Short
  • UShort
  • Int
  • UInt
  • Long
  • ULong
  • Float
  • Double
  • String
  • Data Lists(存储其他 DataTokens)
  • Data Dictionaries(存储其他 DataTokens)
  • Object Reference(通过装箱能够存储任何东西,但不能被序列化)
  • Data Errors(一个指示出错原因的枚举)

属性​

| 属性 | 结果 |

函数​

函数结果
TokenType返回此 DataToken 包含的变量的当前 TokenType
IsNumber如果 DataToken 包含任何数字类型,则返回 true。否则返回 false。
IsNull如果此 DataToken 包含的值以任何形式为 null,则返回 true。数字和布尔值永远不会为 null,TokenType.Null 始终为 null,字符串检查 null 但不检查空值,引用使用 Utilities.IsValid 内部处理已离开的玩家和已销毁的对象。
Boolean如果 DataToken 包含布尔值,则返回 bool。否则抛出异常。
Number如果 DataToken 包含任何数字类型,则返回 double。否则抛出异常。
SByte如果 DataToken 包含 sbyte,则返回 8 位有符号 sbyte。否则抛出异常。
Byte如果 DataToken 包含 byte,则返回 8 位无符号 byte。否则抛出异常。
Short如果 DataToken 包含 short、sbyte 或 byte,则返回 16 位有符号 short。否则抛出异常。
UShort如果 DataToken 包含 ushort 或 byte,则返回 16 位无符号 ushort。否则抛出异常。
Int如果 DataToken 包含 int、sbyte、byte、short 或 ushort,则返回 32 位有符号 int。否则抛出异常。
UInt如果 DataToken 包含 uint、byte 或 ushort,则返回 32 位无符号 uint。否则抛出异常。
Long如果 DataToken 包含 long、sbyte、byte、short、ushort 或 uint,则返回 64 位有符号 long。否则抛出异常。
ULong如果 DataToken 包含 ulong、byte、ushort 或 uint,则返回 64 位无符号 ulong。否则抛出异常。
Float如果 DataToken 包含 float、sbyte、byte、short、ushort、int、uint、long 或 ulong,则返回 32 位 float。否则抛出异常。
Double如果 DataToken 包含 double 或任何其他数字类型,则返回 32 位 double。否则抛出异常。
String如果 DataToken 包含字符串,则返回 string。否则抛出异常。
DataDictionary如果 DataToken 包含 Data Dictionary,则返回 Data Dictionary。否则抛出异常。
DataList如果 DataToken 包含 Data List,则返回 Data List。否则抛出异常。
Reference如果 DataToken 包含对象引用,则返回对象引用。否则抛出异常。
Error返回与此令牌关联的错误。否则返回 DataError.None。与其他属性不同,访问此属性永远不会抛出异常。如果你从非错误的令牌访问 Error,它将简单地返回 DataError.None。

函数​

函数结果
Bitcast重用 DataToken 中的现有数据但更改类型。类似于 C++ 中的 reinterpret_cast 或 C# 中的 BitConverter。如果目标类型小于源类型,将截断该值;如果目标类型大于源类型,则零扩展。仅适用于原始类型并返回副本。
ToString将令牌的内容转换为字符串。与访问 String 属性不同,此函数始终成功,因为它将使用底层值的 ToString
GetHashCode返回令牌内容的哈希码。主要用于字典键的内部操作。
CompareTo将此令牌与另一个令牌比较,如果另一个令牌较大则返回 -1,如果相等则返回 0,如果另一个令牌较小则返回 1。容器(如列表和字典)将按计数比较。比较两个类型不同且不是数值的令牌时,将使用 TokenType 枚举的顺序。

创建 Data Token​

UdonSharp​

在 UdonSharp 中,DataToken 可以"隐式"创建,这意味着当函数需要 DataToken 时,你不需要执行 new DataToken(value)。相反,你可以直接传入值,它会自动为你创建 DataToken。

DataToken 创建
// 你可以这样做
DataToken _explicitFloat = new DataToken(5.3f);
DataToken _explicitInt = new DataToken(5);
DataToken _explicitString = new DataToken("value");
DataToken _explicitBool = new DataToken(true);

// 但这更简单
DataToken _float = 5.3f;
DataToken _Int = 5;
DataToken _String = "value";
DataToken _Bool = true;
warning

不要在 UdonSharp 中使用 nameof() 隐式创建 DataToken,否则可能会遇到错误。

Udon Graph​

在 Udon Graph 中,你需要使用 DataToken Implicit 或 DataToken Constructor 节点来创建包含值的 DataToken。 data-tokens-7GAcVrY.png

从 Data Token 获取值​

在从 DataToken 获取值之前,你需要确保其包含的类型,因为如果你尝试提取不兼容的类型,它将停止你的 UdonBehaviour。有几种方法可以确保包含的类型与你想要提取的类型兼容。

  • 你可以检查 DataToken.TokenType 属性以获取确切类型
  • 从 Data List 或 Data Dictionary 检索值时,你可以使用 TryGetValue 并指定 TokenType。如果 TokenType 不正确,该函数将返回 false。
  • 你可以检查 DataToken.IsNumber 属性以确定它是否为数字。如果是,那么你可以安全地提取 Number 属性,它将从实际类型向上转换为 double。如果类型是 long 或 ulong,可能会丢失精度。
  • 无论令牌的类型如何,ToString 始终是一个有效选项,永远不会抛出错误。
U# 中的 DataToken 检索
// 如果我们知道它是一个字符串,我们可以安全地从令牌中提取字符串
if (unknownToken.TokenType == TokenType.String)
{
Debug.Log(unknownToken.String);
}

// 我们可以使用 IsNumber 来查看它是否是某种数字,即使我们不知道是哪种
if (unknownToken.IsNumber)
{
Debug.Log(unknownToken.Number);
}

// 如果我们从容器中提取值,我们可以使用进行自身类型检查的版本
if (dictionary.TryGetValue("key", TokenType.String, out DataToken value))
{
Debug.Log(value.String);
}

data-tokens-SqQqE5w.png

一旦你确定类型正确,可以通过访问值属性(如 DataToken.Float 和 DataToken.Boolean)从 DataToken 中获取值。每个类型都有自己的属性,可用于提取该特定类型。

如果你对数据有完全控制权,可以跳过所有 TokenType 检查,直接从 token 中获取值。这可以节省一些额外代码,但如果数据来自外部源或类型可能为其他值,请确保不要这样做。

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

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

错误​

当对 Data List 或 Data Dictionary 的操作失败并返回 DataToken 时,它们会生成错误 token。错误 token 包含一个枚举(分类错误类型)和一个字符串(更具体地说明错误)。并非所有错误都会生成字符串,因为有些错误无需进一步说明。

如果你有错误 token,可以使用 DataToken.Error 获取错误枚举,使用 DataToken.String 获取消息。你也可以使用 DataToken.ToString(),它会自动将枚举和字符串组合成一个完整的消息,这使得直接调用 Debug.Log(token) 非常方便。

错误 token 可以是以下几种不同类型:

值含义
KeyDoesNotExist你尝试访问数据字典中的键,但该键不存在。
IndexOutOfRange你尝试访问数据数组中的索引,但该索引小于 0 或大于等于数组的计数。
TypeMismatch你尝试访问值,但该值不是你期望的类型。注意,这仅在你使用接受类型的 TryGetValue 版本时才会发生。
TypeUnsupported数据容器具有你尝试使用的序列化格式不支持的的类型。如果你将引用 token 放入数据容器,然后尝试将其序列化为 Json,就会发生这种情况。
ValueUnsupported数据容器具有你尝试使用的序列化格式不支持的值。如果你将 NaN 或 Infinity 浮点数放入数据容器,然后尝试将其序列化为 Json,就会发生这种情况。
UnableToParse无法解析序列化格式。如果源 Json 无效,就会发生这种情况。
使用 TokenType 的 TryGetValue
if (dictionary.TryGetValue("key", TokenType.Float out DataToken value)) {
// 如果 TryGetValue 成功,我们可以对 token 进行操作
Debug.Log($"成功获取值 {token.Float}");
} else {
// 如果 TryGetValue 失败,token 将是一个错误
Debug.Log($"获取值失败,错误:{token.Error}");
}

data-tokens-zcqKePv.png

常见问题​

String 和 ToString 有什么区别?​

DataToken.String 和 DataToken.ToString() 相似但并非完全相同,因为 DataToken.String 是专门访问 DataToken 内部的字符串值,而 DataToken.ToString() 是将任何存在的内容转换为字符串。

因此,无论 DataToken 包含什么内容,ToString 始终有效,永远不会停止你的 UdonBehaviour。如果它包含一个 bool,那么它会返回 true 或 false。如果它包含一个数字,它会使用 ToString("G", CultureInfo.InvariantCulture) 创建该数字的字符串表示。

另一方面,只有当 DataToken 包含字符串时,访问 DataToken.String 才有效。如果 DataToken 包含一个 float 而你尝试访问 DataToken.String,则会抛出异常并停止你的 UdonBehaviour。

DataError 的独特之处在于它同时包含一个 Error 枚举和一个字符串。建议在 DataError 上使用 ToString(),因为 ToString() 会将枚举和字符串组合成一条包含错误和错误原因的完整消息。

为什么不提供直接返回值的 TryGetValue,跳过 token?​

如果有一个 TryGetValue 方法直接提供值而不必使用 token,那将会很方便。这是一个合理的问题,因为不断从容器中检索 token 然后再单独访问容器中的值确实很繁琐。我们考虑了几种简化此过程的方案,其中已实现的一种是在 TryGetValue 中包含内置的类型检查。

另一种考虑的方案是创建一个泛型版本的 TryGetValue,通过 T 参数指定系统类型。虽然 UdonSharp 支持这种方法(至少在静态方法中),但 Udon 本身不支持。此外,虽然这个方案有优势,但在发生错误时会阻止通过 DataToken 返回 DataError,导致返回默认值。最终我们选择不实现这种方法,因为它会向用户隐藏错误,使问题更难以识别。幸运的是,用户可以利用 UdonSharp 泛型、静态方法和扩展方法的可用性,创建自己的泛型值检索解决方案。