c# - 如何将可序列化的 HashSet 添加到 Unity/C#?
问题描述
我需要一个可以在 Inspector 中编辑的 HashSet。
我找到了这个解决方案......
在这里发布...
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 ()
{
var cur = new HashSet<T> (values);
foreach (var val in this) {
if (!cur.Contains (val)) {
values.Add (val);
}
}
}
public void OnAfterDeserialize ()
{
Clear ();
foreach (var val in values)
{
if (val != null)
Add (val);
}
}
}
但是它会引发以下2个错误...
NullReferenceException: Object reference not set to an instance of an object
System.Collections.Generic.HashSet`1+Enumerator[T].MoveNext () (at <351e49e2a5bf4fd6beabb458ce2255f3>:0)
SerializableHashSet`1[T].OnBeforeSerialize () (at Assets/Scripts/Utilities/Collections/SerializableHashSet.cs:23)
ArgumentNullException: Value cannot be null.
Parameter name: array
System.Array.Clear (System.Array array, System.Int32 index, System.Int32 length) (at <695d1cc93cca45069c528c15c9fdd749>:0)
System.Collections.Generic.HashSet`1[T].Clear () (at <351e49e2a5bf4fd6beabb458ce2255f3>:0)
SerializableHashSet`1[T].OnAfterDeserialize () (at Assets/Scripts/Utilities/Collections/SerializableHashSet.cs:32)
我也不确定我是否需要在第 21 行进行检查 - 首先肯定不会在内部 HashSet 中出现重复项?
解决方案
一般来说,简单地从集合类型继承有点危险。
老实说甚至不确定,但我认为这里的问题与序列化程序没有正确调用构造函数有关。
相反,我会(即使它当然需要更多工作)有一个包含两个字段 aList<T>
和 a的包装类HashSet<T>
,然后实现相同的接口,将它们转发到HashSet
字段,例如
[Serializable]
public class SerializableHashSet<T> :
ISerializationCallbackReceiver,
ISet<T>,
IReadOnlyCollection<T>
{
[SerializeField] private List<T> values = new List<T>();
private HashSet<T> _hashSet = new HashSet<T>();
#region Constructors
// empty constructor required for Unity serialization
public SerializableHashSet() { }
public SerializableHashSet(IEnumerable<T> collection)
{
_hashSet = new HashSet<T>(collection);
}
#endregion Constructors
#region Interface forwarding to the _hashset
public int Count => _hashSet.Count;
public bool IsReadOnly => false;
public bool ISet<T>.Add(T item) => _hashSet.Add(item);
public bool ICollection<T>.Remove(T item) => _hashSet.Remove(item);
public void ExceptWith(IEnumerable<T> other) => _hashSet.ExceptWith(other);
public void IntersectWith(IEnumerable<T> other) => _hashSet.IntersectWith(other);
public bool IsProperSubsetOf(IEnumerable<T> other) => _hashSet.IsProperSubsetOf(other);
public bool IsProperSupersetOf(IEnumerable<T> other) => _hashSet.IsProperSupersetOf(other);
public bool IsSubsetOf(IEnumerable<T> other) => _hashSet.IsSubsetOf(other);
public bool IsSupersetOf(IEnumerable<T> other) => _hashSet.IsSupersetOf(other);
public bool Overlaps(IEnumerable<T> other) => _hashSet.Overlaps(other);
public bool SetEquals(IEnumerable<T> other) => _hashSet.SetEquals(other);
public void SymmetricExceptWith(IEnumerable<T> other) => _hashSet.SymmetricExceptWith(other);
public void UnionWith(IEnumerable<T> other) => _hashSet.UnionWith(other);
public void Clear() => _hashSet.Clear();
public bool Contains(T item) => _hashSet.Contains(item);
public void CopyTo(T[] array, int arrayIndex) => _hashSet.CopyTo(array, arrayIndex);
Collection<T>.Add(T item) => _hashSet.Add(item);
public IEnumerator<T> GetEnumerator() => _hashSet.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
#endregion Interface forwarding to the _hashset
#region ISerializationCallbackReceiver implemenation
public void OnBeforeSerialize()
{
var cur = new HashSet<T>(values);
foreach (var val in this)
{
if (!cur.Contains(val))
{
values.Add(val);
}
}
}
public void OnAfterDeserialize()
{
Clear();
foreach (var val in values)
{
Add(val);
}
}
#endregion ISerializationCallbackReceiver implemenation
}
如果这是我无法判断的最佳方法,但它毫无例外地有效;)
请注意
在检查器中,您仍然可以有重复的条目,这些条目将被 HashSet 简单地忽略。在 HashSet 中删除的条目也将保留在 List 中!
此限制来自您的ISerializationCallbackReceiver
实施。之所以这样做是因为 Inspector 在添加元素时只是复制了最后一个条目,否则根本不可能通过 Inspector 添加元素。除了实现自定义抽屉之外,没有真正的解决方法......
推荐阅读
- html - 如何在引导程序中删除垂直空格行
- r - 绘制 mtry 和 n_tree 值以及准确性
- angular - 将 T1 类型的数组映射到 T2 类型的数组
- list - 如何在序言中将'Saya'翻译成'I'和'saya'翻译成'i'
- javascript - webpack 在前端和后端都可以工作吗?
- postgresql - 如何返回函数内定义的一组 cte 行?
- swift - 在 glkview 前置摄像头显示镜像问题
- flutter - Flutter 自定义叠加层
- java - 在edittext android Pie中检测语言类型?
- c++ - 如何在配置类型设置为动态链接库的解决方案中使用静态库?