我需要一本字典给海关检查员

本文关键字:字典 检查员 一本 | 更新日期: 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无法序列化本机词典。在脚本中使用此类可以在检查器中公开字典。

  • 源自词典
  • 使用任何可序列化类型作为键或值
  • 无需创建自定义检查器

我的计划是看看它是否仍然有效。我的想法是,它给了我们一个建设的起点。