Data Dictionaries
Data Dictionaryは、Data Tokenをキーと値のペアで保存するコンテナであり、C#のDictionaryと同様のものです。Data Dictionaryの関数の多くは、内部的なC# Dictionaryのラッパーであるため、より詳細な情報が必要な場合はC# Dictionaryのドキュメントも併せて参照してください。
Data Dictionaryのキーと値は、どちらもData Tokenです。つまり、実質的にあらゆるものをキーとして使用できます。ただし、VRCJSONへのシリアライズを予定している場合は、文字列型のキーのみがサポートされています。
UdonSharpを使用する場合は、using VRC.SDK3.Data; ディレクティブを追加してData Dictionaryを使用してください。
プロパティ
| プロパティ | Result |
|---|---|
Count | 辞書内の要素数を取得します。 |
関数
| 関数 | Input | Output | Result |
|---|---|---|---|
Add | DataToken key, DataToken value | 指定したキーに値を設定します。SetValueと異なり、キーが既に存在する場合は例外がスローされるのがこの関数の主な目的です。初期化時に使用すると重複を検知するのに役立ちますが、通常の用途では実行時エラーが発生してUdonBehaviourが停止する可能性があるため、推奨されません。 | |
Clear | この辞書からすべてのキーと値を削除します。 | ||
ContainsKey | DataToken key | bool Result | 指定したキーがこの辞書に存在する場合、trueを返します。 |
ContainsValue | DataToken key | bool Result | 指定したキーがこの辞書に存在する場合、trueを返します。 |
DeepClone | DataDictionary Result | DataDictionaryを、すべての同じ値を含む新しいDataDictionaryに複製します。ShallowCloneとは異なり、DeepCloneは各DataListやDataDictionaryの内部を再帰的に走査し、その内容もコピーすることを意味します。TokenTypeが"Reference"であるアイテムは、配列を含め、元のオブジェクトと同じ参照を保持し、ディープコピーはされません。 | |
GetKeys | DataList keys | このData Dictionary内に存在するすべてのキーのData Listを返します。これを使用して、forループ内でData Dictionary内のすべての項目を反復処理します。 | |
GetValues | DataList values | このData Dictionary内に存在するすべての値のData Listを返します。 | |
Remove | DataToken key | bool success | この辞書から特定のキーを削除します。何かが正常に削除された場合はtrueを返します。 |
Remove | DataToken key | bool success, DataToken value | この辞書から特定のキーを削除します。何かが正常に削除された場合はtrueを返します。削除に成功した場合、削除された値がout DataTokenにコピーされます。 |
SetValue | DataToken key, DataToken value | 指定したキーの値を設定します。そのキーが存在しない場合は、新しく追加されます。 | |
ShallowClone | DataDictionary result | DataDictionaryを、同じ値をすべて含む新しいDataDictionaryに複製します。DeepCloneとは異なり、DataDictionaryが他のDataListやDataDictionaryを含んでいる場合、それらは同じ参照のままとなります。 | |
TryGetValue | DataToken key | bool success, DataToken output | 指定されたキーからトークンを取得し、out DataTokenに格納します。取得に成功した場合はtrue、失敗した場合はfalseを返します。取得に失敗した場合、結果の代わりにout DataTokenにDataErrorが格納されます。 |
TryGetValue | DataToken key, TokenType expected | bool success, DataToken output | 指定されたキーからトークンを取得し、out DataTokenに格納します。取得に成功した場合はtrue、失敗した場合はfalseを返します。取得に失敗した場合、結果の代わりにout DataTokenにDataErrorが格納されます。このバージョンのTryGetValueにはTokenTypeが含まれており、自動的に型チェックが行われます。型が一致しない場合はfalseを返し、DataError.TypeMismatchが設定されます。 |
Jsonから生成されたDataDictionaryに対して、ContainsValue、ShallowClone、GetValuesのように、すべての値に影響を与えたり、すべての値を参照したりする関数を呼び出す際は注意してください。これらを呼び出すと、まだ解析されていない最上位レベルのすべての値が解析されるため、値が多数ある場合は負荷が高くなる可能性があります。一度解析されれば、その後の操作は低コストになります。
DataDictionaryから値を取得する
辞書から値を取得する方法はいくつかあります。それぞれに適した用途があるため、どれを使用するかは状況に応じて選択してください。
TryGetValue
辞書から安全に値を取得したいが、その場所にどの型が存在するかを気にしない場合は、TryGetValueを使用することをお勧めします。これは、値の取得に成功したかどうかに応じてtrueまたはfalseを返す関数です。成功時と失敗時の処理が明確になるよう、ifやbranchの条件式の中に配置することを想定しています。
if (dictionary.TryGetValue("key", out DataToken value)) {
Debug.Log($"Success! {value}");
} else {
Debug.Log($"Failed! {value}");
}
失敗した場合、受け取るDataToken自体は有効ですが、データが含まれる代わりにエラーが含まれます。
このメソッドは、特定の場所から値を取得したいものの、それが正確に何であるかを気にしない場合に適しています。
この関数には型チェックが組み込まれていないため、if、branch、switchなどを用いて、何らかの形で型チェックを行う必要があります。特定の型のみを対象とする場合は、自動的に型チェックを行ってくれるTokenTypeを指定するバージョンのTryGetValueを使用することをお勧めします。
TokenTypeを指定したTryGetValue
辞書から値を取得したいものの、その値がどの型であるか不明な場合は、型チェックを行うことが重要です。コード内で独自に実装することもできますが、複雑になりがちです。その代わりに、TokenTypeを含んだバージョンの TryGetValue を使用することができます。これを使用すると、期待する型である場合にのみ値を取得するように指定できます。型が一致しない場合はfalseを返し、適切に処理を行うことができます。
このメソッドは、特定の場所から特定の値を取得したいが、データが外部ソース由来であるため、そのソースに正しいデータが含まれているか確信が持てない場合に適しています。
// You could do it this way, but it's a bit ugly
if (dictionary.TryGetValue("key", out DataToken value)) {
if (value.TokenType == TokenType.DataDictionary)
{
Debug.Log($"Success! Matching dictionary has {value.DataDictionary.Count} items");
}
}
// This approach has a type check built in! It's functionally the same, but streamlined.
if (dictionary.TryGetValue("key", TokenType.DataDictionary, out value)) {
Debug.Log($"Success! Matching dictionary has {value.DataDictionary.Count} items");
}
ブラケットによる短縮構文
UdonSharpでは dictionary["key"] = "value"; のようなブラケット構文を、Udonグラフでは DataDictionary Get Item ノードを使用して、Data Dictionaryに対してアイテムの設定や取得を行うことができます。この方法はより簡潔で使いやすいですが、完全に安全ではない点に注意してください。存在しないキーから値を取得しようとするなど、無効な操作を実行しようとすると、UdonBehaviourが停止する可能性があります。
このメソッドは、データの内容を完全に把握しており、その存在と期待する型が保証されている場合に適しています。それ以外の場合は、何らかの形の TryGetValue を使用することが推奨されます。
dictionary["A"] = 5;
dictionary["B"] = 10;
// This makes the assumption that A and B will always contain integers.
// This is a safe assumption to make since we set them just above in a controlled environment.
// If the data is coming from an external source, we shouldn't make these assumptions!
int sum = dictionary["A"].Int + dictionary["B"].Int;
DataDictionaryの初期化
UdonSharpでは、DataDictionaryをプライベート変数で初期化できます。これにより、コードが実行される前に定義された既存のデータセットを持つことが可能になります。また、ネストされた辞書や、DataTokenがサポートするあらゆる型にも対応しています。この構文の使用例を以下に示します:
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 ループを使用してすべてのキーを反復処理し、各キーの値にアクセスできます。
// First get all the keys in the dictionary
DataList keys = dictionary.GetKeys();
// For loop over all the keys
for (int i = 0; i < keys.Count; i++)
{
// Get the key at the current index
DataToken key = keys[i];
// Access the entry connected to that key
Debug.Log(dictionary[key].ToString());
}
GetKeys() は一見すると処理負荷が高そうに見えるかもしれませんが、キーが追加または削除されない限りキャッシュされるため、Udon 自体のオーバーヘッドを除けば、頻繁にアクセスしても概してパフォーマンスは良好です。
辞書をソートされた状態で反復処理する必要がある場合、このメソッドを活用した巧妙なテクニックとして、GetKeys() でキーを取得してから Sort() で並べ替え、その後に for ループで処理する方法があります。辞書自体には順序という概念がなくソートすることはできませんが、アクセスに使用するリストをソートすることは可能です!
辞書の値をキーとは別に扱いたい場合は、 GetValues() を使用することもできます。これはすべての値を明示的に展開する必要があるアプリケーションでは有用ですが、辞書に慣れていない場合、このメソッドは誤解を招く可能性があることに注意してください。他の理由として、辞書には順序がないため、GetValues() で取得した際に特定のアイテムが常に特定のインデックスにあると想定してはいけません。また、これらのインデックスが GetKeys() で取得されるインデックスと一致すると期待すべきでもありません。ほとんどの場合、GetKeys() だけで十分であり、GetValues() は辞書に対してより高度な制御が必要な場合のための選択肢です。
Data Dictionaryをネットワーク経由で他のプレイヤーと同期する
Data Dictionaryを直接同期することはできません。ただし、VRCJsonを使用してJSON文字列への Serialize および Deserialize を行うことは可能です。これが、UdonSyncを使用してData Dictionaryを同期するための現在の推奨方法です。
これを行う一つの方法は、OnPreSerialization と OnDeserialization を使用して、json 文字列の Serialize および Deserialize を行うことです。この手法を用いれば、コードの他の部分でシリアル化について気にする必要はなく、単純に値を設定するだけで済みます。
[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());
}
}
最終更新: