所以我需要一个字典结构来保存自定义编辑器中的内容。我知道不允许使用词典,因此我尝试创建自己的类型,发现不允许使用泛型。但是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),但我试图删除泛型,希望它能工作。它没有。游戏开始后不会保存这些值。
PS:这个“public List m_customDictionary;”工作正常,但它没有我想要的功能。有任何想法吗?谢谢!
答案 0 :(得分:1)
看起来Unity有一个问题,即识别此类是从List派生的。作为一种解决方法,我建议将List作为成员而不是派生。如果您希望能够遍历CustomDictionary,则可以添加简单地返回List GetEnumerator()的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
派生的虚拟定义为GenericDictionary
子类,那将是理想的。事实证明,我们无法做到。 Unity不会在编辑器中显示这样的类。因此,我在此处使用的解决方案是手动创建必须从Entry
派生的GenericDictionaryItem
类型,并将其作为第三个GenericDictionary
泛型参数传递,除了Key和Value。
接下来,在C#中,我们不能使用泛型类型Entry
非默认构造函数。因此,我用于此的解决方案是将SetKV(key, value)
方法添加到GenericDictionaryItem
,可以将其用作构造函数。 Entry
将仅使用默认构造函数。也许更优雅的解决方案是使用一些工厂对象,我只是快速做到了。
最后,请注意您无法将通用密钥与==
进行比较,必须使用Equals(Object)
。重要的是要记住,因为参考类型的equals默认实现是参考比较。因此它可以与值类型(int,Color,Vector3 ...)以及字符串一起使用,但如果您决定使用{Equals
,则必须为GameObject
实现GameObject
{1}}作为关键。 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"));
}
}
答案 1 :(得分:0)
虽然有点晚,但我在Unity资源商店中找到了这个 免费 资产。它是在4.7版本的时候添加的。它是SerializableDictionary。 Unity的可序列化字典类。
从商店出发:
Unity无法序列化本机词典。在脚本中使用此类可以在检查器中公开字典。
我的计划是看它是否仍然有效。我的想法是,它为我们提供了建立的起点。