版本:Unity 6 (6000.0)
语言英语
  • C#

SerializedProperty.Next

建议更改

成功!

感谢您帮助我们提高 Unity 文档的质量。虽然我们无法接受所有提交,但我们确实会阅读用户提出的每个建议,并在适用情况下进行更新。

关闭

提交失败

由于某些原因,您的建议更改无法提交。请<a>稍后再试</a>。感谢您抽出时间帮助我们提高 Unity 文档的质量。

关闭

取消

声明

public bool Next(bool enterChildren);

描述

移动到下一个属性。

更新 SerializedProperty,使其指向下一个属性(按序列化顺序)。

这可以用于遍历对象的状态,而无需事先了解其数据结构。它也是迭代数组的有效方法。

如果当前属性是复合类型,例如结构体、数组、字符串或内联 Unity 结构体(如 Vector3),则 enterChildren 参数决定是访问嵌套属性,还是跳过到复合类型后面的属性。

Next 永远不会移动到不同的对象,因此当当前属性类型为 SerializedPropertyType.ObjectReference 时,调用 Next(true) 不会访问该引用对象的属性。访问引用对象的一种方法是使用 SerializedProperty.objectReferenceValue 获取该对象,并为该目标构造一个新的 SerializedObject 实例。

如果 SerializedObject 中没有更多属性,则此方法将返回 false,并且 SerializedProperty 将设置为无效状态,不再引用任何属性。

using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEditor;

public class SerializePropertyNextExample : ScriptableObject { public long m_dataFirst = 45; public string m_data2 = "hi";

[Serializable] class NestedInline { public long[] m_data3 = new long[] {1, 2}; public bool m_data4 = true; } [SerializeField] NestedInline m_nested;

[MenuItem("Example/SerializedProperty Next Example")] static void NextExample() { var scriptableObject = ScriptableObject.CreateInstance<SerializePropertyNextExample>();

using (var serializedObject = new SerializedObject(scriptableObject)) { var sb = new StringBuilder();

// Visit the top level (depth 0) properties: m_dataFirst, m_data2 and m_nested VisitAll(serializedObject, false, sb);

// Visit each property, down to the granularity of individual string and array elements VisitAll(serializedObject, true, sb); Debug.Log(sb.ToString()); } }

static void VisitAll(SerializedObject serializedObject, bool visitChildren, StringBuilder report) { report.AppendLine($"Traversal result (visitChildren: {visitChildren})");

// Start at the first field, instead of using GetIterator(), in order to skip past the internal properties var serializedProperty = serializedObject.FindProperty("m_dataFirst"); do { report.AppendLine($"\tFound {serializedProperty.propertyPath} (depth {serializedProperty.depth})"); } while (serializedProperty.Next(visitChildren)); } }
using System;
using System.Text;
using UnityEngine;
using UnityEditor;

public class SerializePropertyArrayEnumerationWithNextExample : ScriptableObject { // Example of enumerating the elements of an array of struct using Next() [Serializable] public struct Data { public int m_Data1; public string m_Data2; }

public Data[] m_Data; public bool m_BeyondData;

[MenuItem("Example/SerializedProperty Array Enumeration Using Next")] static void TestArrayEnumeration() { var scriptableObject = ScriptableObject.CreateInstance<SerializePropertyArrayEnumerationWithNextExample>(); scriptableObject.m_Data = new Data[] { new Data() { m_Data1 = 0, m_Data2 = "zero" }, new Data() { m_Data1 = 1, m_Data2 = "one" }, new Data() { m_Data1 = 2, m_Data2 = "two" } };

var report = new StringBuilder();

using (var serializedObject = new SerializedObject(scriptableObject)) { var arrayProperty = serializedObject.FindProperty("m_Data");

var arrayElement = arrayProperty.GetArrayElementAtIndex(0); var arraySize = arrayProperty.arraySize;

for (int i = 0; i < arraySize; i++) { ReportElement(arrayElement, i, report); arrayElement.Next(false); } } Debug.Log(report.ToString()); }

static void ReportElement(SerializedProperty arrayElement, int index, StringBuilder report) { var data1 = arrayElement.FindPropertyRelative("m_Data1").intValue; var data2 = arrayElement.FindPropertyRelative("m_Data2").stringValue; report.AppendLine($"Visiting Index {index}: {data1}, {data2}"); } }

如果 SerializedProperty 引用的是 ManagedReference(例如,带有 SerializeReference 属性的字段),并且 Next 被调用,enterChildren 设置为 true,则 SerializedProperty 将移动到托管对象中的第一个属性。由于托管引用可以形成循环图,因此盲目调用 Next(true) 可能会导致无限循环。以下示例演示了避免这种情况的技术。

using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEditor;

[CreateAssetMenu] public class SerializePropertyNextSerializeReferenceExample : ScriptableObject { [Serializable] public class Node { public int m_Data; public string m_Data2;

[SerializeReference] public Node m_Child1 = null;

[SerializeReference] public Node m_Child2 = null; }

[SerializeReference] public Node m_Front = null;

[MenuItem("Example/SerializedProperty Next Cyclic Graph Example")] static void TestNextOnCyclicGraph() { var scriptableObject = ScriptableObject.CreateInstance<SerializePropertyNextSerializeReferenceExample>();

// Setup a graph of 3 nodes that has several cycles scriptableObject.m_Front = new Node() { m_Data = 1, m_Data2 = "First Node" };

var node2 = new Node() { m_Data = 2, m_Data2 = "Second Node", m_Child1 = scriptableObject.m_Front }; scriptableObject.m_Front.m_Child1 = node2;

var node3 = new Node() { m_Data = 3, m_Data2 = "Third Node" }; scriptableObject.m_Front.m_Child2 = node3; node2.m_Child2 = node3; node3.m_Child1 = scriptableObject.m_Front;

using (var serializedObject = new SerializedObject(scriptableObject)) { var serializedProperty = serializedObject.FindProperty("m_Front");

var sb = new StringBuilder(); sb.AppendLine("Graph traversal result:");

// Track visited Node objects by managed reference id to prevent endless looping var visitedNodes = new HashSet<long>();

bool visitChild; do { // default is false so we don't enumerate each character of each string, visitChild = false;

if (serializedProperty.propertyType == SerializedPropertyType.ManagedReference) { long refId = serializedProperty.managedReferenceId; if (visitedNodes.Add(refId)) visitChild = true; // First time seeing node, so visit it } else if (serializedProperty.propertyType == SerializedPropertyType.String) { sb.AppendLine($"Found {serializedProperty.propertyPath} : {serializedProperty.stringValue}"); } else if (serializedProperty.propertyType == SerializedPropertyType.Integer) { sb.AppendLine($"Found {serializedProperty.propertyPath} : {serializedProperty.intValue}"); } } while (serializedProperty.Next(visitChild));

/*Expected output Graph traversal result: Found m_Front.m_Data : 1 Found m_Front.m_Data2 : First Node Found m_Front.m_Child1.m_Data : 2 Found m_Front.m_Child1.m_Data2 : Second Node Found m_Front.m_Child1.m_Child2.m_Data : 3 Found m_Front.m_Child1.m_Child2.m_Data2 : Third Node */ Debug.Log(sb.ToString()); } } }