我需要一本字典给海关检查员
本文关键字:字典 检查员 一本 | 更新日期: 2023-09-27 18:20:45
所以我需要一个字典结构来将内容保存在自定义编辑器中。我知道字典是不允许的,所以我尝试创建自己的类型,发现这两个泛型都不允许。但List是。因此,从这个开始,我创建了一个扩展List的类,并添加了一个小功能,允许我将此对象视为Dictionary。这是代码:
public class CustomDictionary : List<CustomDictionaryItem>
{
public void Add (string key, UnityEngine.Object value)
{
this.Add (new CustomDictionaryItem (key, value));
}
public bool ContainsKey (string key)
{
return this.Any (sdi => sdi.Key == key);
}
public bool Remove (string key)
{
return this.RemoveAll (sdi => sdi.Key == key) != 0;
}
public UnityEngine.Object this [string key] {
get {
if (ContainsKey (key))
return (UnityEngine.Object)this.First (sdi => sdi.Key == key).Value;
return null;
}
set {
if (ContainsKey (key)) {
CustomDictionaryItem item = this.First (sdi => sdi.Key == key);
item.Value = value;
} else
Add (key, value);
}
}
public List<string> Keys {
get {
return this.Select (sdi => sdi.Key).ToList ();
}
}
public List<UnityEngine.Object> Values {
get {
return this.Select (sdi => (UnityEngine.Object)sdi.Value).ToList ();
}
}
}
[Serializable]
public class CustomDictionaryItem
{
[SerializeField]
private string m_key;
public string Key{ get { return m_key; } set { m_key = value; } }
[SerializeField]
private UnityEngine.Object m_value;
public UnityEngine.Object Value{ get { return m_value; } set { m_value = value; } }
public CustomDictionaryItem (string key, UnityEngine.Object value)
{
m_key = key;
m_value = value;
}
}
老实说,我希望它是类型化的(而不是UnityEngine.Object),但我试图删除泛型,希望它能起作用。事实并非如此。游戏开始后不会保存这些值。
附言:这个"public List m_customDictionary;"可以正常工作,但没有我想要的功能。有什么想法吗?非常感谢。
Unity似乎在识别这个类是从List派生的时遇到了问题。作为一种变通方法,我建议将List作为成员,而不是派生。如果您希望能够对CustomDictionary进行迭代,则可以添加GetEnumerator()方法,该方法只返回List GetEnumerator.()。
[Serializable]
public class CustomDictionary
{
[SerializeField]
private List<CustomDictionaryItem> l;
public void Add (string key, UnityEngine.Object value)
{
l.Add (new CustomDictionaryItem (key, value));
}
public bool ContainsKey (string key)
{
return l.Any (sdi => sdi.Key == key);
}
public bool Remove (string key)
{
return l.RemoveAll (sdi => sdi.Key == key) != 0;
}
public UnityEngine.Object this [string key] {
get {
if (ContainsKey (key))
return (UnityEngine.Object)l.First (sdi => sdi.Key == key).Value;
return null;
}
set {
if (ContainsKey (key)) {
CustomDictionaryItem item = l.First (sdi => sdi.Key == key);
item.Value = value;
} else
Add (key, value);
}
}
public List<string> Keys {
get {
return l.Select (sdi => sdi.Key).ToList ();
}
}
public List<UnityEngine.Object> Values {
get {
return l.Select (sdi => (UnityEngine.Object)sdi.Value).ToList ();
}
}
public List<CustomDictionaryItem>.Enumerator GetEnumerator() {
return l.GetEnumerator();
}
}
编辑:经过思考,有一种方法可以使这本词典通用。它很丑陋,我甚至为写这样的代码感到有点羞愧,但它会起作用的。
诀窍如下:Unity不会在编辑器中显示泛型类。但是它对派生自泛型类的类没有问题。因此,我们的想法是创建一个从GenericDictionary
派生的伪非泛型字典。与GenericDictionaryItem
相同,我们将创建简单地从泛型伪派生的非泛型伪。
这是基本的想法。以下是陷阱:
如果我们可以将从GenericDictionaryItem
派生的dummy定义为GenericDictionary
子类,那将是理想的。事实证明我们做不到。Unity不会在编辑器中显示此类类。因此,我在这里使用的解决方案是手动创建一个必须从GenericDictionaryItem
派生的Entry
类型,并将其作为除Key和Value之外的第三个GenericDictionary
泛型参数传递。
接下来,在C#中,我们不能使用泛型类型Entry
非默认构造函数。因此,我使用的解决方案是将SetKV(key, value)
方法添加到GenericDictionaryItem
中,该方法可以用作构造函数。Entry
将仅使用默认构造函数。也许一个更优雅的解决方案是使用一些工厂对象,我只是做得很快。
最后,请注意您不能将泛型密钥与==
进行比较,必须使用Equals(Object)
。重要的是要记住,因为equals引用类型的默认实现是引用比较。因此,它可以很好地处理值类型(int、Color、Vector3…)和字符串,但如果您决定使用GameObject
作为键,则必须为引用键类型(如GameObject
)实现Equals
。C#Dictionary
为这种情况提供了IEqualityComparer
,这里我没有提供,但如果你真的愿意,你可以做。
最后,代码是:
[Serializable]
public class GenericDictionaryItem<K, V>
{
[SerializeField]
private K m_key;
public K Key{ get { return m_key; } set { m_key = value; } }
[SerializeField]
private V m_value;
public V Value{ get { return m_value; } set { m_value = value; } }
public GenericDictionaryItem (K key, V value)
{
m_key = key;
m_value = value;
}
public GenericDictionaryItem () : this (default(K), default(V)) {
}
// In C# we cannot use non default constructor with types passed as generic argument.
// So as a workaround we will use this function instead of constructor.
internal void SetKV(K key, V value) {
m_key = key;
m_value = value;
}
}
[Serializable]
// Generic arguments: K - key type, V - value type, Entry - must be a class derived from
// GenericDictionaryItem<K, V> and implementing a default constructor.
// K must implement Equals method!
public class GenericDictionary<K, V, Entry> where Entry : GenericDictionaryItem<K, V>, new()
{
[SerializeField]
private List<Entry > l;
public void Add (K key, V value)
{
// Cannot use non default constructor with generic type.
// This is a workaround
var e = new Entry();
e.SetKV(key, value);
l.Add (e);
}
public bool ContainsKey (K key)
{
return l.Any (sdi => sdi.Key.Equals(key));
}
public bool Remove (K key)
{
return l.RemoveAll (sdi => sdi.Key.Equals(key)) != 0;
}
public V this [K key] {
get {
if (ContainsKey (key))
return (V)l.First (sdi => sdi.Key.Equals(key)).Value;
return default(V);
}
set {
if (ContainsKey (key)) {
Entry item = l.First (sdi => sdi.Key.Equals(key));
item.Value = value;
} else
Add (key, value);
}
}
public List<K> Keys {
get {
return l.Select (sdi => sdi.Key).ToList ();
}
}
public List<V> Values {
get {
return l.Select (sdi => (V)sdi.Value).ToList ();
}
}
public List<Entry>.Enumerator GetEnumerator() {
return l.GetEnumerator();
}
}
// Test class
public class DictionaryTest : MonoBehaviour {
// Create dummy entry type
[Serializable]
public class Entry : GenericDictionaryItem<string, UnityEngine.Object> {
}
// Create dummy dictionary type
// Pay attention - we pass 3 arguments key type, value type and entry type
[Serializable]
public class MyDictionary : GenericDictionary<string, UnityEngine.Object, Entry> {
}
// And now we will see the dictionary in editor
public MyDictionary d;
void Start() {
d.Add("test1", null);
d.Add("test2", null);
Debug.Log(d.ContainsKey("test1"));
Debug.Log(d.ContainsKey("test2"));
Debug.Log(d.ContainsKey("test3"));
}
}
虽然有点晚,但我刚刚在Unity资产商店中找到了这个免费资产。它是在4.7版本前后添加的。它是SerializableDictionary。Unity的可序列化字典类。
来自商店:
Unity无法序列化本机词典。在脚本中使用此类可以在检查器中公开字典。
- 源自词典
- 使用任何可序列化类型作为键或值
- 无需创建自定义检查器
我的计划是看看它是否仍然有效。我的想法是,它给了我们一个建设的起点。