PropertyVisitor를 사용하여 프로퍼티 방문자 생성
Follow this example to create a property visitor with the PropertyVisitor base class.
읽는 시간 8분최근 업데이트: 한 달 전
이 예시는 기본 클래스를 사용하여 프로퍼티 방문자를 생성하는 방법을 보여 줍니다. 및 인터페이스를 사용하는 동일한 예시는 로우레벨 API를 사용하여 프로퍼티 방문자 생성을 참고하십시오.
PropertyVisitorIPropertyBagVisitorIPropertyVisitor개요 예시
이 예시에는 오브젝트의 현재 상태를 콘솔에 출력하는 프로퍼티 방문자를 만드는 단계별 명령이 포함되어 있습니다.
다음 유형이 있다고 가정해 봅시다.
public class Data{ public string Name = "Henry"; public Vector2 Vec2 = Vector2.one; public List<Color> Colors = new List<Color> { Color.green, Color.red }; public Dictionary<int, string> Dict = new Dictionary<int, string> {{5, "zero"}};}
다음과 같이 유틸리티 메서드 를 생성합니다.
DebugUtilitiespublic static class DebugUtilities{ public static void PrintObjectDump<T>(T value) { // Magic goes here. }}
다음과 같이 오브젝트로 메서드를 호출합니다.
DataPrintObjectDumpDebugUtilities.PrintObjectDump(new Data());
그러면 콘솔에 다음이 출력됩니다.
- Name {string} = Henry- Vec2 {Vector2} = (1.00, 1.00)- Colors {List<Color>} - [0] = {Color} RGBA(0.000, 1.000, 0.000, 1.000) - [1] = {Color} RGBA(1.000, 0.000, 0.000, 1.000)- Dict {Dictionary<int, string>} - [5] {KeyValuePair<int, string>} - Key {int} = 5 - Value {string} = five
방문자 생성
먼저 클래스를 생성합니다. 클래스 내에서 StringBuilder를 사용하여 오브젝트의 현재 상태를 나타내는 문자열을 만듭니다.
DumpObjectVisitor-
에서 상속하는
PropertyVisitor클래스를 생성합니다.DumpObjectVisitor -
클래스에필드를 추가합니다.
StringBuilder -
를 지우고 들여쓰기 레벨을 재설정하는
StringBuilder메서드를 추가합니다.Reset -
오브젝트의 현재 상태를 나타내는 문자열을 반환하는메서드를 추가합니다.
GetDump완료된 클래스는 다음과 같습니다.// `PropertyVisitor` is an abstract class that you must subclass from it. public class DumpObjectVisitor: PropertyVisitor{ private const int k_InitialIndent = 0; private readonly StringBuilder m_Builder = new StringBuilder(); private int m_IndentLevel = k_InitialIndent; private string Indent => new (' ', m_IndentLevel * 2); public void Reset() { m_Builder.Clear(); m_IndentLevel = k_InitialIndent; } public string GetDump() { return m_Builder.ToString(); }}
최상위 프로퍼티 가져오기
DumpObjectVisitorVisitPropertyPropertyVisitor-
클래스 내에 다음 오버라이드
DumpObjectVisitor메서드를 추가합니다.VisitPropertyprotected override void VisitProperty<TContainer, TValue>(Property<TContainer, TValue> property, ref TContainer container, ref TValue value){ m_Builder.AppendLine($"- {property.Name}");} -
이제 방문자가 거의 없으므로 유틸리티 메서드를 구현할 수 있습니다.클래스의
DebugUtilities메서드를 업데이트하여 새PrintObjectDump인스턴스를 생성하고 이를 사용하여 특정 오브젝트의 프로퍼티를 방문합니다.DumpObjectVisitorpublic static class DebugUtilities{ private static readonly DumpObjectVisitor s_Visitor = new (); public static void PrintObjectDump<T>(T value) { s_Visitor.Reset(); // This is the main entry point to run a visitor. PropertyContainer.Accept(s_Visitor, ref value); Debug.Log(s_Visitor.GetDump()); }}그러면 다음과 같은 결과가 출력됩니다.- Name- Vec2- Colors- Dict
하위 프로퍼티 가져오기
이전 섹션의 출력은 메서드를 오버라이드할 때 오브젝트의 하위 프로퍼티를 자동으로 방문하지 않음을 나타냅니다. 하위 프로퍼티를 가져오려면 메서드를 사용하여 각 값에 방문자를 재귀적으로 적용합니다.
VisitPropertyPropertyContainer.Accept-
클래스 내에서
DebugUtilities메서드를 업데이트하여 중첩할 값에 방문자가 재귀적으로 적용합니다.VisitPropertyprotected override void VisitProperty<TContainer, TValue>(Property<TContainer, TValue> property, ref TContainer container, ref TValue value){ m_Builder.AppendLine($"{Indent}- {property.Name}"); ++m_IdentLevel; // Apply this visitor recursively on the value to nest in. if (null != value) PropertyContainer.Accept(this, ref value); --m_IdentLevel;}그러면 다음과 같은 결과가 출력됩니다.- Name- Vec2- x- y- Colors- 0 - r - g - b - a- 1 - r - g - b - a - Dict- 5 - Key - Value
각 프로퍼티에 대한 세부 정보 표시
다음으로 컬렉션 항목의 프로퍼티 이름과 각 프로퍼티의 유형 및 값을 가져오겠습니다.
특정 프로퍼티는 특별한 이름을 가지며 특히 컬렉션 항목을 처리할 때 그렇습니다. 다음은 프로퍼티 이름에 대한 규칙입니다.
- 리스트 항목의 경우 이름은 인덱스에 해당합니다.
- 딕셔너리의 경우 이름은 키 값의 문자열 버전에서 파생됩니다.
- 세트의 경우 이름은 값의 문자열 버전에 기반합니다.
이를 더 명시적으로 구별하려면 프로퍼티 이름을 대괄호로 묶으십시오.
-
클래스 내에 다음 메서드를 추가합니다.
DumpObjectVisitorprivate static string GetPropertyName(IProperty property){ return property switch { // You can also treat `IListElementProperty`, `IDictionaryElementProperty`, and `ISetElementProperty` separately. ICollectionElementProperty => $"[{property.Name}]", _ => property.Name };} -
을 사용하여 특정 유형의 표시 이름을 가져오도록
TypeUtility.GetTypeDisplayName메서드를 업데이트합니다.VisitPropertyprotected override void VisitProperty<TContainer, TValue>(Property<TContainer, TValue> property, ref TContainer container, ref TValue value){ var propertyName = GetPropertyName(property); // Get the concrete type of the property or its declared type if value is null. var typeName = TypeUtility.GetTypeDisplayName(value?.GetType() ?? property.DeclaredValueType()); m_Builder.AppendLine($"{Indent}- {propertyName} = {{{typeName}}} {value}"); ++m_IndentLevel; if (null != value) PropertyContainer.Accept(this, ref value); --m_IndentLevel;}그러면 다음과 같은 결과가 출력됩니다.- Name = {string} Henry- Vec2 = {Vector2} (1.00, 1.00)- x = {float} 1- y = {float} 1- Colors = {List<Color>} System.Collections.Generic.List`1[UnityEngine.Color]- [1] = {Color} RGBA(0.000, 1.000, 0.000, 1.000) - r = {float} 0 - g = {float} 1 - b = {float} 0 - a = {float} 1- [1] = {Color} RGBA(1.000, 0.000, 0.000, 1.000) - r = {float} 1 - g = {float} 0 - b = {float} 0 - a = {float} 1- Dict = {Dictionary<int, string>} System.Collections.Generic.Dictionary`2[System.Int32,System.String]- [5] = {KeyValuePair<int, string>} [5, five] - Key = {int} 5 - Value = {string} five
컬렉션 유형에 표시되는 정보의 양 줄이기
List<T>ToString()System.Collections.Generic.List1[UnityEngine.Color]TypeTraits.IsContainerVisitPropertyDumpObjectVisitorTypeTraits.IsContainerVisitPropertyprotected override void VisitProperty<TContainer, TValue>(Property<TContainer, TValue> property, ref TContainer container, ref TValue value){ var propertyName = GetPropertyName(property); var type = value?.GetType() ?? property.DeclaredValueType(); var typeName = TypeUtility.GetTypeDisplayName(type); // Only display the values for primitives, enums and strings. if (TypeTraits.IsContainer(type)) m_Builder.AppendLine($"{Indent}- {propertyName} {{{typeName}}}"); else m_Builder.AppendLine($"{Indent}- {propertyName} = {{{typeName}}} {value}"); ++m_IndentLevel; if (null != value) PropertyContainer.Accept(this, ref value); --m_IndentLevel;}
그러면 다음과 같은 결과가 출력됩니다.
- Name = {string} Henry- Vec2 {Vector2}- x = {float} 1- y = {float} 1- Colors {List<Color>}- [0] {Color} - r = {float} 0 - g = {float} 1 - b = {float} 0 - a = {float} 1- [1] {Color} - r = {float} 1 - g = {float} 0 - b = {float} 0 - a = {float} 1- Dict {Dictionary<int, string>}- [5] {KeyValuePair<int, string>} - Key = {int} 5 - Value = {string} five
팁:
표시되는 정보의 양을 줄이기 위해 다음 메서드를 사용하여 컬렉션 유형의 전문화를 오버라이드할 수도 있습니다.
Visitprotected override void VisitCollection<TContainer, TCollection, TElement>(Property<TContainer, TCollection> property, ref TContainer container, ref TCollection value) {}protected override void VisitList<TContainer, TList, TElement>(Property<TContainer, TList> property, ref TContainer container, ref TList value) {}protected override void VisitDictionary<TContainer, TDictionary, TKey, TValue>(Property<TContainer, TDictionary> property, ref TContainer container, ref TDictionary value) {}protected override void VisitSet<TContainer, TSet, TValue>(Property<TContainer, TSet> property, ref TContainer container, ref TSet value) {}
이들은 메서드와 유사하지만 각 컬렉션 유형의 일반 파라미터를 노출합니다.
VisitProperty유형별 오버라이드 추가
IVisitPropertyAdapterPropertyVisitorVisitPropertyDumpObjectVisitorVector2ColorIVisitPropertyAdapterpublic class DumpObjectVisitor : PropertyVisitor , IVisitPropertyAdapter<Vector2> , IVisitPropertyAdapter<Color>{ public DumpObjectVisitor() { AddAdapter(this); } void IVisitPropertyAdapter<Vector2>.Visit<TContainer>(in VisitContext<TContainer, Vector2> context, ref TContainer container, ref Vector2 value) { var propertyName = GetPropertyName(context.Property); m_Builder.AppendLine($"{Indent}- {propertyName} = {{{nameof(Vector2)}}} {value}"); } void IVisitPropertyAdapter<Color>.Visit<TContainer>(in VisitContext<TContainer, Color> context, ref TContainer container, ref Color value) { var propertyName = GetPropertyName(context.Property); m_Builder.AppendLine($"{Indent}- {propertyName} = {{{nameof(Color)}}} {value}"); }}
완료된 클래스는 다음과 같습니다.
DumpObjectVisitorpublic class DumpObjectVisitor: PropertyVisitor, IVisitPropertyAdapter<Vector2>, IVisitPropertyAdapter<Color> { private const int k_InitialIndent = 0; // StringBuilder to store the dumped object's properties and values. private readonly StringBuilder m_Builder = new StringBuilder(); private int m_IndentLevel = k_InitialIndent; // Helper property to get the current indentation. private string Indent => new (' ', m_IndentLevel * 2); public DumpObjectVisitor() { // Constructor, it initializes the DumpObjectVisitor and adds itself as an adapter // to handle properties of type Vector2 and Color. AddAdapter(this); } // Reset the visitor, clearing the StringBuilder and setting indentation to initial level. public void Reset() { m_Builder.Clear(); m_IndentLevel = k_InitialIndent; } // Get the string representation of the dumped object. public string GetDump() { return m_Builder.ToString(); } // Helper method to get the property name, handling collections and other property types. private static string GetPropertyName(IProperty property) { return property switch { // If it's a collection element property, display it with brackets ICollectionElementProperty => $"[{property.Name}]", // For other property types, display the name as it is _ => property.Name }; } // This method is called when visiting each property of an object. // It determines the type of the value and formats it accordingly for display. protected override void VisitProperty<TContainer, TValue>(Property<TContainer, TValue> property, ref TContainer container, ref TValue value) { var propertyName = GetPropertyName(property); // Get the type of the value or property. var type = value?.GetType() ?? property.DeclaredValueType(); var typeName = TypeUtility.GetTypeDisplayName(type); // Only display the values for primitives, enums, and strings, and treat other types as containers. if (TypeTraits.IsContainer(type)) m_Builder.AppendLine($"{Indent}- {propertyName} {{{typeName}}}"); else m_Builder.AppendLine($"{Indent}- {propertyName} = {{{typeName}}} {value}"); // Increase indentation level before visiting child properties (if any). ++m_IndentLevel; if (null != value) PropertyContainer.Accept(this, ref value); // Decrease indentation level after visiting child properties. --m_IndentLevel; } // This method is a specialized override for Vector2 properties. // It displays the property name and its value as a Vector2. void IVisitPropertyAdapter<Vector2>.Visit<TContainer>(in VisitContext<TContainer, Vector2> context, ref TContainer container, ref Vector2 value) { var propertyName = GetPropertyName(context.Property); m_Builder.AppendLine($"{Indent}- {propertyName} = {{{nameof(Vector2)}}} {value}"); } // This method is a specialized override for Color properties. // It displays the property name and its value as a Color. void IVisitPropertyAdapter<Color>.Visit<TContainer>(in VisitContext<TContainer, Color> context, ref TContainer container, ref Color value) { var propertyName = GetPropertyName(context.Property); m_Builder.AppendLine($"{Indent}- {propertyName} = {{{nameof(Color)}}} {value}"); }}
하위 프로퍼티의 현재 상태 인쇄
기본적으로 데이터에서 방문자를 실행하면 해당 오브젝트에 대한 방문이 직접 시작됩니다. 모든 프로퍼티 방문자에 대해 오브젝트의 하위 등록 정보에 대한 방문을 시작하려면 메서드에 를 전달합니다.
PropertyContainer.AcceptPropertyPath-
메서드를 업데이트하여 선택적
DebugUtilities를 사용합니다.PropertyPathpublic static class DebugUtilities{ private static readonly DumpObjectVisitor s_Visitor = new(); public static void PrintObjectDump<T>(T value, PropertyPath path = default) { s_Visitor.Reset(); if (path.IsEmpty) PropertyContainer.Accept(s_Visitor, ref value); else PropertyContainer.Accept(s_Visitor, ref value, path); Debug.Log(s_Visitor.GetDump()); }}