如何序列化 HashSet 以便在检查器中使用

问题描述 投票:0回答:2

Unity版本:2017.3.1f1

我正在尝试在 Unity 检查器中使用

HashSet
。具体来说,我需要一些
MonoBehaviour
s 来拥有
HashSet
字段,这些字段可以通过检查器修改它们的内容。

为了实现这个目标,我创建了一个具体的类,它是

HashSet
的子类,并在内部使用
List
进行(反)序列化,其方式与本指南中的字典非常相似:

但是我遇到了列表显示在检查器中的问题,但我不能在其中设置超过 1 个值。如果我将列表的大小设置为 2 或更大,它会立即设置回 1。

在尝试调试问题时,我发现

OnBeforeSerialize
(和not
OnAfterDeserialize
)每帧都在执行,不断重置值。我不确定为什么将其设置为 1。

请注意,如果我在 1 个可用插槽中输入一个字符串,它不会被重置。因此,这种方法目前对于 0 或 1 个字符串的

HashSet
是“有效的”,但不会更多。此外,如果我使用
HashSet
字段而不是从它继承(就像上面链接中所做的那样),结果也不会改变。

这是一个最小的例子:

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

public class TEST : MonoBehaviour
{
    public StringHashSet test;
}

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

    public void OnBeforeSerialize ()
    {
        values.Clear ();

        foreach (T val in this) {
            values.Add (val);
        }
    }

    public void OnAfterDeserialize ()
    {
        this.Clear ();

        foreach (T val in values) {
            this.Add (val);
        }
    }
}

[System.Serializable]
public class StringHashSet : SerializableHashSet<string>
{
}
  1. 我怎样才能让它按预期工作(任意大小的字符串列表被(反)序列化为
    HashSet
    )?
  2. 另外,为什么
    OnBeforeSerialize
    每帧都被执行,即使没有在检查器中进行任何更改?

更多信息


我发现这是因为当列表的大小发生变化时,默认情况下列表中的所有新元素都与前一个元素具有相同的值,因此在

HashSet
中都将被压缩为1值。

因此,虽然上面的问题 2 仍然存在,但问题 1 已经演变为寻求解决方法,同时保持所需的

HashSet
功能。

c# unity3d serialization
2个回答
3
投票

考虑到添加到问题中更多信息部分的发现,这是我解决这个问题的方法:

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

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

    public void OnBeforeSerialize ()
    {
        var cur = new HashSet<T> (values);

        foreach (var val in this) {
            if (!cur.Contains (val)) {
                values.Add (val);
            }
        }
    }

    public void OnAfterDeserialize ()
    {
        this.Clear ();

        foreach (var val in values) {
            this.Add (val);
        }
    }
}

[System.Serializable]
public class StringHashSet : SerializableHashSet<string>
{
}

OnAfterDeserialize
是相同的。

OnBeforeSerialize
不再清除
values
列表。相反,它将新值从
HashSet
添加到
List
- 具体来说,存在于
HashSet
但不存在于
List
.

中的值

这允许在检查器中将新元素添加到列表时继续功能:重复和空白条目将正常工作,因为列表不会在任何时候被清除,只会添加到。


关于第二个问题,我找到了关于为什么

OnBeforeSerialize
如此频繁执行的信息:http://answers.unity.com/answers/796853/view.html

相关信息:

如果对象的检查器在编辑器中打开,则调用每一帧


0
投票

我也需要在 Inspector 中有一个唯一的对象列表并且遇到了这个问题,这就是我解决它的方法:

宣布你的名单:

public List<T> list = new List<T>();

创建一个 OnValidate 方法:

   private void OnValidate() {
        int count = list.Count;

        // remove duplicates
        HashSet<T> uniqueList = new HashSet<T>(list);

        list= uniqueList.ToList();

        if (count > list.Count) {
            list.Add(null);
        }
    }

希望对某人有所帮助。

© www.soinside.com 2019 - 2024. All rights reserved.