You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity/C#如何实现可在Inspector编辑的可序列化HashSet并解决相关报错

可序列化HashSet修复方案

报错根因

  • NullReferenceException:Unity序列化回调执行时机早于HashSet基类的完全初始化,此时遍历HashSet的枚举器会访问未初始化的内部字段触发空引用。
  • ArgumentNullException:同样是初始化未完成的状态下调用Clear()方法,HashSet内部存储数组为null,触发空参数异常。
  • 原有第21行的重复校验逻辑完全冗余,且原有序列化逻辑为增量往List添加元素,会导致已经从HashSet中移除的元素残留在序列化List中,造成数据不一致。

修复后完整实现

using System;
using System.Collections.Generic;
using UnityEngine;

[Serializable]
public class SerializableHashSet<T> : HashSet<T>, ISerializationCallbackReceiver
{
    [SerializeField] 
    private List<T> values = new List<T>();

    public SerializableHashSet() : base() {}
    
    public SerializableHashSet(IEnumerable<T> collection) : base(collection) {}

    public void OnBeforeSerialize ()
    {
        // 清空旧数据,直接用当前HashSet的内容覆盖List,保证完全同步
        values.Clear();
        foreach (var val in this)
        {
            values.Add(val);
        }
    }
    
    public void OnAfterDeserialize ()
    {
        // 先清空当前HashSet,加异常捕获兼容未初始化的极端情况
        try
        {
            Clear();
        }
        catch (ArgumentNullException)
        {
            // 基类未初始化完成时,先初始化内部存储结构
            Initialize(0);
        }
        
        // 把序列化List的内容写入HashSet,自动去重
        foreach (var val in values)
        {
            // 兼容值类型和引用类型的默认值过滤
            if (EqualityComparer<T>.Default.Equals(val, default)) continue;
            Add(val);
        }
    }
}

如果需要在Inspector中正常显示和编辑,还需要添加对应的PropertyDrawer,把HashSet当成普通List渲染:

#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;

[CustomPropertyDrawer(typeof(SerializableHashSet<>))]
public class SerializableHashSetDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        var valuesProp = property.FindPropertyRelative("values");
        EditorGUI.PropertyField(position, valuesProp, label, true);
    }

    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        var valuesProp = property.FindPropertyRelative("values");
        return EditorGUI.GetPropertyHeight(valuesProp, label, true);
    }
}
#endif

使用说明

直接声明对应类型的字段即可,示例:

public class TestScript : MonoBehaviour
{
    public SerializableHashSet<string> stringHashSet;
    public SerializableHashSet<int> intHashSet;
}

在Inspector中编辑时会自动处理重复值,运行时读写和原生HashSet完全一致。


内容的提问来源于stack exchange,提问作者PhilB

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 08:36:04