2025-10-28 15:36:55 +09:00
|
|
|
#nullable enable
|
|
|
|
|
using Gpm.Ui;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
|
|
|
|
namespace UVC.UI.List.Tree
|
|
|
|
|
{
|
2025-10-28 20:10:51 +09:00
|
|
|
/// <summary>
|
|
|
|
|
/// 트리 구조 리스트에서 각 아이템이 갖는 데이터 클래스입니다.
|
|
|
|
|
///
|
|
|
|
|
/// 트리 구조란? 폴더-파일처럼 상위(부모)와 하위(자식) 관계를 가진 계층 구조입니다.
|
|
|
|
|
/// 예: 📁 폴더
|
|
|
|
|
/// ├─ 📄 파일1
|
|
|
|
|
/// ├─ 📄 파일2
|
|
|
|
|
/// └─ 📁 하위폴더
|
|
|
|
|
/// └─ 📄 파일3
|
|
|
|
|
///
|
|
|
|
|
/// 이 클래스는 InfiniteScrollData를 상속하여 UI 스크롤 리스트와 연동됩니다.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class TreeListItemData
|
2025-10-28 15:36:55 +09:00
|
|
|
{
|
2025-10-28 20:10:51 +09:00
|
|
|
#region 이벤트 (Events)
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 데이터가 변경되었을 때 발생하는 이벤트입니다.
|
|
|
|
|
///
|
|
|
|
|
/// 용도: 이 데이터의 속성(Name, Option 등)이 변경되면
|
|
|
|
|
/// UI에 자동으로 반영되도록 통지합니다.
|
|
|
|
|
///
|
|
|
|
|
/// 사용 예:
|
|
|
|
|
/// treeItem.OnDataChanged += (data) => Debug.Log("데이터 변경됨!");
|
|
|
|
|
/// </summary>
|
|
|
|
|
public Action<TreeListItemData>? OnDataChanged;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 선택 상태가 변경되었을 때 발생하는 이벤트입니다.
|
|
|
|
|
///
|
|
|
|
|
/// 용도: 사용자가 이 아이템을 클릭해서 선택 또는 선택 해제했을 때
|
|
|
|
|
/// 다른 시스템(예: 옵션 창)에 알려줍니다.
|
|
|
|
|
///
|
|
|
|
|
/// 매개변수:
|
|
|
|
|
/// - TreeListItemData: 변경된 아이템 자신
|
|
|
|
|
/// - bool: true면 선택됨, false면 선택 해제됨
|
|
|
|
|
///
|
|
|
|
|
/// 사용 예:
|
|
|
|
|
/// treeItem.OnSelectionChanged += (data, isSelected) => {
|
|
|
|
|
/// Debug.Log($"{data.Name}이 {(isSelected ? "선택됨" : "해제됨")}");
|
|
|
|
|
/// };
|
|
|
|
|
/// </summary>
|
|
|
|
|
public Action<TreeListItemData, bool>? OnSelectionChanged;
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region 내부 필드 (Private Fields)
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 아이템의 이름을 저장하는 비공개 필드입니다.
|
|
|
|
|
///
|
|
|
|
|
/// '_' 접두사를 붙인 이유:
|
|
|
|
|
/// 실제 데이터는 여기 저장하고, public 프로퍼티(Name)를 통해
|
|
|
|
|
/// 접근을 제어합니다. (캡슐화)
|
|
|
|
|
///
|
|
|
|
|
/// 예: _name = "폴더", Name 프로퍼티로 접근
|
|
|
|
|
/// </summary>
|
|
|
|
|
private string _name = string.Empty;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 아이템의 추가 옵션 정보를 저장합니다.
|
|
|
|
|
///
|
|
|
|
|
/// 용도: 읽기 전용, 숨김 속성 등의 추가 설정 정보
|
|
|
|
|
/// 예: "readonly", "hidden", "locked" 등
|
|
|
|
|
/// </summary>
|
|
|
|
|
private string _option = string.Empty;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 이 아이템의 자식들이 펼쳐져 있는지 여부를 나타냅니다.
|
|
|
|
|
///
|
|
|
|
|
/// true: 자식들이 표시됨 (▼ 펼침 상태)
|
|
|
|
|
/// false: 자식들이 숨겨짐 (▶ 접혀있는 상태)
|
|
|
|
|
///
|
|
|
|
|
/// 자식이 없으면 이 값은 의미가 없습니다.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private bool _isExpanded = false;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 현재 아이템이 사용자에게 선택되어 있는지를 나타냅니다.
|
|
|
|
|
///
|
|
|
|
|
/// true: 선택됨 (보통 배경색이 다르게 표시)
|
|
|
|
|
/// false: 선택 안 됨 (기본 상태)
|
|
|
|
|
///
|
|
|
|
|
/// 예: 파일 탐색기에서 파일을 클릭했을 때 그 파일의 _isSelected = true
|
|
|
|
|
/// </summary>
|
|
|
|
|
private bool _isSelected = false;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 이 아이템의 하위 아이템들을 모두 저장하는 리스트입니다.
|
|
|
|
|
///
|
|
|
|
|
/// 트리 구조 예:
|
|
|
|
|
/// 부모 (이 객체)
|
|
|
|
|
/// ├─ 자식1
|
|
|
|
|
/// ├─ 자식2
|
|
|
|
|
/// └─ 자식3
|
|
|
|
|
///
|
|
|
|
|
/// _children 리스트에 [자식1, 자식2, 자식3]이 저장됩니다.
|
|
|
|
|
/// 자식이 없으면 빈 리스트입니다.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private List<TreeListItemData> _children = new List<TreeListItemData>();
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region 공개 프로퍼티 (Public Properties)
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 아이템의 이름을 가져오거나 설정합니다.
|
|
|
|
|
///
|
|
|
|
|
/// 동작:
|
|
|
|
|
/// - 가져올 때(get): _name의 값을 반환합니다.
|
|
|
|
|
/// - 설정할 때(set):
|
|
|
|
|
/// 1. 기존 값과 비교해서 정말 달라졌는지 확인
|
|
|
|
|
/// 2. 다르면 새 값으로 변경
|
|
|
|
|
/// 3. OnDataChanged 이벤트를 발생시켜 UI에 알림
|
|
|
|
|
///
|
|
|
|
|
/// 이렇게 하는 이유: 같은 값으로 변경되는 불필요한 갱신을 피합니다.
|
|
|
|
|
///
|
|
|
|
|
/// 사용 예:
|
|
|
|
|
/// treeItem.Name = "새로운 이름"; // 자동으로 UI 업데이트
|
|
|
|
|
/// string currentName = treeItem.Name; // 이름 읽기
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string Name
|
|
|
|
|
{
|
|
|
|
|
get => _name;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (_name != value)
|
|
|
|
|
{
|
|
|
|
|
_name = value;
|
|
|
|
|
NotifyDataChanged(); // UI에 변경을 알림
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-28 15:36:55 +09:00
|
|
|
/// <summary>
|
2025-10-28 20:10:51 +09:00
|
|
|
/// 아이템의 추가 옵션을 가져오거나 설정합니다.
|
|
|
|
|
///
|
|
|
|
|
/// 동작: Name 프로퍼티와 동일하게 작동합니다.
|
|
|
|
|
/// 변경될 때마다 OnDataChanged 이벤트를 발생시킵니다.
|
|
|
|
|
///
|
|
|
|
|
/// 사용 예:
|
|
|
|
|
/// treeItem.Option = "readonly"; // 읽기 전용으로 설정
|
2025-10-28 15:36:55 +09:00
|
|
|
/// </summary>
|
2025-10-28 20:10:51 +09:00
|
|
|
public string Option
|
|
|
|
|
{
|
|
|
|
|
get => _option;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (_option != value)
|
|
|
|
|
{
|
|
|
|
|
_option = value;
|
|
|
|
|
NotifyDataChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-10-28 15:36:55 +09:00
|
|
|
|
|
|
|
|
/// <summary>
|
2025-10-28 20:10:51 +09:00
|
|
|
/// 이 아이템의 자식들이 펼쳐져 있는지 여부를 가져오거나 설정합니다.
|
|
|
|
|
///
|
|
|
|
|
/// internal 접근제한자 이유:
|
|
|
|
|
/// 이것은 UI 시스템에서만 관리해야 하므로 외부에서 직접 접근할 수 없습니다.
|
|
|
|
|
/// (같은 어셈블리 내부에서만 접근 가능)
|
|
|
|
|
///
|
|
|
|
|
/// true: 자식들이 표시됨 (트리 펼침)
|
|
|
|
|
/// false: 자식들이 숨겨짐 (트리 접힘)
|
|
|
|
|
///
|
|
|
|
|
/// 사용 예: (UI 시스템에서만)
|
|
|
|
|
/// treeItem.IsExpanded = true; // 자식들을 표시
|
2025-10-28 15:36:55 +09:00
|
|
|
/// </summary>
|
2025-10-28 20:10:51 +09:00
|
|
|
internal bool IsExpanded
|
|
|
|
|
{
|
|
|
|
|
get => _isExpanded;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (_isExpanded != value)
|
|
|
|
|
{
|
|
|
|
|
_isExpanded = value;
|
|
|
|
|
NotifyDataChanged(); // 트리 구조 UI 갱신
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-10-28 15:36:55 +09:00
|
|
|
|
|
|
|
|
/// <summary>
|
2025-10-28 20:10:51 +09:00
|
|
|
/// 이 아이템이 선택되어 있는지 여부를 가져오거나 설정합니다.
|
|
|
|
|
///
|
|
|
|
|
/// 중요한 차이점: 다른 프로퍼티는 OnDataChanged를 호출하지만,
|
|
|
|
|
/// 이것은 OnSelectionChanged를 호출합니다.
|
|
|
|
|
/// 왜? 선택 상태는 UI 갱신이 아니라
|
|
|
|
|
/// 선택 이벤트 처리가 필요하기 때문입니다.
|
|
|
|
|
///
|
|
|
|
|
/// 사용 예:
|
|
|
|
|
/// treeItem.IsSelected = true; // 아이템 선택
|
|
|
|
|
/// if (treeItem.IsSelected) { ... } // 선택 여부 확인
|
2025-10-28 15:36:55 +09:00
|
|
|
/// </summary>
|
2025-10-28 20:10:51 +09:00
|
|
|
public bool IsSelected
|
|
|
|
|
{
|
|
|
|
|
get => _isSelected;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (_isSelected != value)
|
|
|
|
|
{
|
|
|
|
|
_isSelected = value;
|
|
|
|
|
// OnSelectionChanged 이벤트 발생
|
|
|
|
|
// 예: "폴더가 선택되었습니다" 같은 처리를 수행
|
|
|
|
|
OnSelectionChanged?.Invoke(this, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-10-28 15:36:55 +09:00
|
|
|
|
|
|
|
|
/// <summary>
|
2025-10-28 20:10:51 +09:00
|
|
|
/// 사용자가 확장/축소 버튼을 클릭했을 때 호출될 함수입니다.
|
|
|
|
|
///
|
|
|
|
|
/// 용도: 트리의 화살표(▼/▶) 버튼을 클릭했을 때
|
|
|
|
|
/// IsExpanded 상태를 변경하는 로직을 실행합니다.
|
|
|
|
|
///
|
|
|
|
|
/// 누가 등록하나? UI 시스템 (TreeListItem 클래스)
|
|
|
|
|
///
|
|
|
|
|
/// 사용 예:
|
|
|
|
|
/// treeItem.OnClickAction = (data) => {
|
|
|
|
|
/// data.IsExpanded = !data.IsExpanded; // 펼침/접힘 토글
|
|
|
|
|
/// };
|
2025-10-28 15:36:55 +09:00
|
|
|
/// </summary>
|
|
|
|
|
public Action<TreeListItemData>? OnClickAction;
|
|
|
|
|
|
2025-10-28 20:10:51 +09:00
|
|
|
/// <summary>
|
|
|
|
|
/// 이 아이템의 모든 자식 아이템들을 가져오거나 설정합니다.
|
|
|
|
|
///
|
|
|
|
|
/// internal 접근제한자 이유:
|
|
|
|
|
/// 자식 리스트는 AddChild, RemoveChild, ClearChildren 메서드로만
|
|
|
|
|
/// 수정되어야 데이터 일관성이 보장됩니다.
|
|
|
|
|
///
|
|
|
|
|
/// 동작:
|
|
|
|
|
/// - 가져올 때(get): _children 리스트 반환
|
|
|
|
|
/// - 설정할 때(set):
|
|
|
|
|
/// 1. null이면 빈 리스트로 설정 (null 방지)
|
|
|
|
|
/// 2. OnDataChanged 이벤트 발생
|
|
|
|
|
///
|
|
|
|
|
/// ?? 연산자 설명:
|
|
|
|
|
/// childrenItemData ?? new List<>()
|
|
|
|
|
/// = childrenItemData가 null이면 빈 리스트 사용
|
|
|
|
|
/// null이 아니면 childrenItemData 사용
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal List<TreeListItemData> Children
|
|
|
|
|
{
|
|
|
|
|
get => _children;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
_children = value ?? new List<TreeListItemData>();
|
|
|
|
|
NotifyDataChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
2025-10-28 15:36:55 +09:00
|
|
|
|
2025-10-28 20:10:51 +09:00
|
|
|
#region 생성자 (Constructors)
|
2025-10-28 15:36:55 +09:00
|
|
|
|
2025-10-28 20:10:51 +09:00
|
|
|
/// <summary>
|
|
|
|
|
/// 빈 TreeListItemData를 생성합니다.
|
|
|
|
|
///
|
|
|
|
|
/// 초기값:
|
|
|
|
|
/// - Name: 빈 문자열
|
|
|
|
|
/// - Option: 빈 문자열
|
|
|
|
|
/// - IsExpanded: false (접혀있음)
|
|
|
|
|
/// - Children: 빈 리스트 (자식 없음)
|
|
|
|
|
///
|
|
|
|
|
/// 사용 예:
|
|
|
|
|
/// var item = new TreeListItemData();
|
|
|
|
|
/// item.Name = "새 폴더";
|
|
|
|
|
/// </summary>
|
|
|
|
|
public TreeListItemData()
|
|
|
|
|
{
|
|
|
|
|
_name = string.Empty;
|
|
|
|
|
_option = string.Empty;
|
|
|
|
|
_isExpanded = false;
|
|
|
|
|
_children = new List<TreeListItemData>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 이름과 선택적으로 자식 목록을 지정하여 TreeListItemData를 생성합니다.
|
|
|
|
|
///
|
|
|
|
|
/// 매개변수:
|
|
|
|
|
/// - generalName: 아이템의 이름 (필수)
|
|
|
|
|
/// - childrenItemData: 초기 자식 목록 (선택사항, null 가능)
|
|
|
|
|
///
|
|
|
|
|
/// 초기값:
|
|
|
|
|
/// - Name: generalName
|
|
|
|
|
/// - Option: 빈 문자열
|
|
|
|
|
/// - IsExpanded: false
|
|
|
|
|
/// - Children: childrenItemData (null이면 빈 리스트)
|
|
|
|
|
///
|
|
|
|
|
/// 사용 예:
|
|
|
|
|
/// // 간단한 아이템 생성
|
|
|
|
|
/// var item1 = new TreeListItemData("폴더");
|
|
|
|
|
///
|
|
|
|
|
/// // 자식을 포함해서 생성
|
|
|
|
|
/// var children = new List<TreeListItemData> { item1 };
|
|
|
|
|
/// var parent = new TreeListItemData("부모 폴더", children);
|
|
|
|
|
/// </summary>
|
2025-10-28 15:36:55 +09:00
|
|
|
public TreeListItemData(string generalName, List<TreeListItemData>? childrenItemData = null)
|
|
|
|
|
{
|
2025-10-28 20:10:51 +09:00
|
|
|
_name = generalName;
|
|
|
|
|
_option = string.Empty;
|
|
|
|
|
_isExpanded = false;
|
|
|
|
|
_children = childrenItemData ?? new List<TreeListItemData>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region 자식 관리 메서드 (Child Management Methods)
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 이 아이템에 자식 아이템을 추가합니다.
|
|
|
|
|
///
|
|
|
|
|
/// 동작:
|
|
|
|
|
/// 1. 자식을 _children 리스트에 추가
|
|
|
|
|
/// 2. OnDataChanged 이벤트 발생 (UI 트리 구조 갱신)
|
|
|
|
|
///
|
|
|
|
|
/// 사용 예:
|
|
|
|
|
/// parent.AddChild(child); // 부모에 자식 추가
|
|
|
|
|
///
|
|
|
|
|
/// 트리 구조 변화:
|
|
|
|
|
/// 변경 전: 변경 후:
|
|
|
|
|
/// 부모 부모
|
|
|
|
|
/// ├─ 자식1 ├─ 자식1
|
|
|
|
|
/// └─ 자식2 ├─ 자식2
|
|
|
|
|
/// └─ 새자식
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void AddChild(TreeListItemData child)
|
|
|
|
|
{
|
|
|
|
|
_children.Add(child);
|
|
|
|
|
NotifyDataChanged(); // UI에 트리 구조 변경 알림
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 이 아이템에서 지정된 자식 아이템을 제거합니다.
|
|
|
|
|
///
|
|
|
|
|
/// 동작:
|
|
|
|
|
/// 1. 자식을 _children 리스트에서 제거
|
|
|
|
|
/// 2. OnDataChanged 이벤트 발생
|
|
|
|
|
///
|
|
|
|
|
/// 주의: 같은 이름의 첫 번째 자식만 제거됩니다.
|
|
|
|
|
/// (TreeListItemData의 == 연산자가 Name으로 비교하기 때문)
|
|
|
|
|
///
|
|
|
|
|
/// 사용 예:
|
|
|
|
|
/// parent.RemoveChild(child); // 부모에서 자식 제거
|
|
|
|
|
///
|
|
|
|
|
/// 트리 구조 변화:
|
|
|
|
|
/// 변경 전: 변경 후:
|
|
|
|
|
/// 부모 부모
|
|
|
|
|
/// ├─ 자식1 ├─ 자식1
|
|
|
|
|
/// ├─ 자식2
|
|
|
|
|
/// └─ 자식3 └─ 자식3
|
|
|
|
|
/// (자식2 제거)
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void RemoveChild(TreeListItemData child)
|
|
|
|
|
{
|
|
|
|
|
_children.Remove(child);
|
|
|
|
|
NotifyDataChanged(); // UI에 트리 구조 변경 알림
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 이 아이템의 모든 자식을 한 번에 제거합니다.
|
|
|
|
|
///
|
|
|
|
|
/// 동작:
|
|
|
|
|
/// 1. _children 리스트를 완전히 비움
|
|
|
|
|
/// 2. OnDataChanged 이벤트 발생
|
|
|
|
|
///
|
|
|
|
|
/// 주의: 자식들이 메모리에서 삭제되는 것은 아니고,
|
|
|
|
|
/// 이 아이템과의 연결만 끊어집니다.
|
|
|
|
|
/// (C#의 가비지 컬렉션이 필요 없으면 나중에 정리)
|
|
|
|
|
///
|
|
|
|
|
/// 사용 예:
|
|
|
|
|
/// parent.ClearChildren(); // 모든 자식 제거
|
|
|
|
|
///
|
|
|
|
|
/// 트리 구조 변화:
|
|
|
|
|
/// 변경 전: 변경 후:
|
|
|
|
|
/// 부모 부모
|
|
|
|
|
/// ├─ 자식1 (모든 자식 제거됨)
|
|
|
|
|
/// ├─ 자식2
|
|
|
|
|
/// └─ 자식3
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void ClearChildren()
|
|
|
|
|
{
|
|
|
|
|
_children.Clear();
|
|
|
|
|
NotifyDataChanged(); // UI에 트리 구조 변경 알림
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region 내부 메서드 (Internal Methods)
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 데이터가 변경되었음을 UI 시스템에 알립니다.
|
|
|
|
|
///
|
|
|
|
|
/// 동작: OnDataChanged 이벤트를 발생시킵니다.
|
|
|
|
|
/// 이를 통해 UI는 자동으로 이 아이템의 정보를 갱신합니다.
|
|
|
|
|
///
|
|
|
|
|
/// 호출되는 시점:
|
|
|
|
|
/// - Name이나 Option이 변경될 때
|
|
|
|
|
/// - 자식이 추가/제거될 때
|
|
|
|
|
/// - IsExpanded 상태가 변경될 때
|
|
|
|
|
///
|
|
|
|
|
/// 왜 protected인가?
|
|
|
|
|
/// 이 클래스를 상속받은 자식 클래스에서도 호출할 수 있도록 하기 위함입니다.
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal void NotifyDataChanged()
|
|
|
|
|
{
|
|
|
|
|
// OnDataChanged가 등록되어 있으면 실행
|
|
|
|
|
// ?. 연산자: null이면 실행하지 않음 (null reference exception 방지)
|
|
|
|
|
OnDataChanged?.Invoke(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region 비교 연산자 (Comparison Operators)
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 두 TreeListItemData 객체가 같은지 비교합니다. (== 연산자)
|
|
|
|
|
///
|
|
|
|
|
/// 비교 기준: Name (아이템의 이름)
|
|
|
|
|
/// 즉, 이름이 같으면 같은 아이템으로 간주합니다.
|
|
|
|
|
///
|
|
|
|
|
/// 비교 로직:
|
|
|
|
|
/// 1. 같은 객체인가? (메모리 주소가 같음) → true
|
|
|
|
|
/// 2. 둘 다 null이거나 하나가 null? → false (둘 다 null이면 true인데, 1번에서 처리)
|
|
|
|
|
/// 3. Name이 같은가? → true/false
|
|
|
|
|
///
|
|
|
|
|
/// 사용 예:
|
|
|
|
|
/// var item1 = new TreeListItemData("파일");
|
|
|
|
|
/// var item2 = new TreeListItemData("파일");
|
|
|
|
|
/// var item3 = new TreeListItemData("폴더");
|
|
|
|
|
///
|
|
|
|
|
/// item1 == item2 // true (Name이 "파일"로 같음)
|
|
|
|
|
/// item1 == item3 // false (Name이 다름)
|
|
|
|
|
/// item1 == null // false
|
|
|
|
|
///
|
|
|
|
|
/// 주의: 같은 이름이면 같은 아이템으로 취급되므로,
|
|
|
|
|
/// 실제로 다른 객체임에도 true가 될 수 있습니다.
|
|
|
|
|
/// 이는 의도된 설계입니다.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static bool operator ==(TreeListItemData? left, TreeListItemData? right)
|
|
|
|
|
{
|
|
|
|
|
// 같은 객체인지 확인 (메모리 주소 비교)
|
|
|
|
|
if (ReferenceEquals(left, right))
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 하나 이상이 null이면 false (ReferenceEquals에서 둘 다 null인 경우는 true 반환)
|
|
|
|
|
if (left is null || right is null)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 이름으로 비교
|
|
|
|
|
return left.Name == right.Name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 두 TreeListItemData 객체가 다른지 비교합니다. (!= 연산자)
|
|
|
|
|
///
|
|
|
|
|
/// 동작: == 연산자의 결과를 반대(!)로 반환합니다.
|
|
|
|
|
///
|
|
|
|
|
/// 사용 예:
|
|
|
|
|
/// if (item1 != item2) { ... } // 다른 아이템이면 실행
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static bool operator !=(TreeListItemData? left, TreeListItemData? right)
|
|
|
|
|
{
|
|
|
|
|
return !(left == right);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region 객체 메서드 (Object Methods)
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 이 객체의 고유한 해시 코드를 반환합니다.
|
|
|
|
|
///
|
|
|
|
|
/// 용도: 이 객체를 Dictionary나 HashSet 같은 컬렉션에 저장할 때 사용합니다.
|
|
|
|
|
///
|
|
|
|
|
/// 해시 코드란? 객체를 빠르게 비교/검색하기 위한 고유 숫자입니다.
|
|
|
|
|
/// 같은 내용이면 같은 해시 코드를 반환해야 합니다.
|
|
|
|
|
///
|
|
|
|
|
/// 우리의 기준:
|
|
|
|
|
/// Name의 해시 코드 = 이 객체의 해시 코드
|
|
|
|
|
/// 왜? == 연산자에서 Name으로 비교하기 때문입니다.
|
|
|
|
|
///
|
|
|
|
|
/// 사용 예:
|
|
|
|
|
/// int hash = item.GetHashCode();
|
|
|
|
|
///
|
|
|
|
|
/// // Dictionary에 저장
|
|
|
|
|
/// Dictionary<TreeListItemData, string> dict = new();
|
|
|
|
|
/// dict[item] = "값"; // 내부적으로 GetHashCode() 사용
|
|
|
|
|
/// </summary>
|
|
|
|
|
public override int GetHashCode()
|
|
|
|
|
{
|
|
|
|
|
return _name.GetHashCode();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 이 객체가 다른 객체와 같은지 비교합니다. (Equals 메서드)
|
|
|
|
|
///
|
|
|
|
|
/// 용도: 모든 C# 객체는 Equals 메서드를 가집니다.
|
|
|
|
|
/// 이 메서드를 오버라이드하여 우리의 비교 로직을 정의합니다.
|
|
|
|
|
///
|
|
|
|
|
/// 동작:
|
|
|
|
|
/// 1. 다른 객체가 TreeListItemData 타입인지 확인
|
|
|
|
|
/// 2. 맞으면 == 연산자로 비교 (Name으로 비교)
|
|
|
|
|
/// 3. 아니면 false 반환
|
|
|
|
|
///
|
|
|
|
|
/// 사용 예:
|
|
|
|
|
/// var item1 = new TreeListItemData("파일");
|
|
|
|
|
/// var item2 = new TreeListItemData("파일");
|
|
|
|
|
///
|
|
|
|
|
/// item1.Equals(item2) // true
|
|
|
|
|
/// item1.Equals("파일") // false (문자열은 다른 타입)
|
|
|
|
|
/// item1 == item2 // true (== 연산자와 동일)
|
|
|
|
|
///
|
|
|
|
|
/// GetHashCode()와 Equals의 관계:
|
|
|
|
|
/// - 같은 객체면 같은 해시 코드를 가져야 함
|
|
|
|
|
/// - 우리의 경우: Name이 같으면 Equals는 true, 해시 코드도 같음
|
|
|
|
|
/// - 이는 일관성 있게 설계되어 있습니다.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public override bool Equals(object? obj)
|
|
|
|
|
{
|
|
|
|
|
// obj가 TreeListItemData 타입인지 확인
|
|
|
|
|
if (obj is TreeListItemData other)
|
2025-10-28 15:36:55 +09:00
|
|
|
{
|
2025-10-28 20:10:51 +09:00
|
|
|
// TreeListItemData면 == 연산자로 비교
|
|
|
|
|
return this == other;
|
2025-10-28 15:36:55 +09:00
|
|
|
}
|
2025-10-28 20:10:51 +09:00
|
|
|
|
|
|
|
|
// 다른 타입이면 false
|
|
|
|
|
return false;
|
2025-10-28 15:36:55 +09:00
|
|
|
}
|
2025-10-28 20:10:51 +09:00
|
|
|
|
|
|
|
|
#endregion
|
2025-10-28 15:36:55 +09:00
|
|
|
}
|
2025-10-28 20:10:51 +09:00
|
|
|
}
|