C#实现类似Lua的table的数据结构

设计意义

Lua的table是使用键值对的方式存取,在CSharp中对应的是字典。但是字典会判断键存在与否,而且使用Add和Remove方法来实现存取改,长期来说确实不方便,使代码看上去不是很整洁。但是我们可以自己封装一个数据结构来魔方table,使用CSharp的索引器和字典结合,再添加一些共用属性和方法即可。但最终不能像table那样装下万物。

接口设计

  1. 键作为索引器,无论是否存在此键,都会返回一个System.Object的对象
点击查看代码
        public Object this[string key]
        {
            get
            {
                Object result = null;
                datas.TryGetValue(key, out result);
                return result;
            }

            set
            {
                if (datas.ContainsKey(key))
                {
                    datas[key] = value;
                }
                else
                {
                    Add(key, value);
                }
            }
        }
  1. 整数作为下标,直接获取整个键值对对象,但不允许修改键
点击查看代码
        public KeyValuePair<string, Object> this[int index]
        {
            get => ElementAt(index);
        }

        private KeyValuePair<string, Object> ElementAt(int index)
        {
            if (index < 0)
            {
                Debug.LogWarning("index must be greater than zero!!!");
                return new KeyValuePair<string, Object>();
            }
            if (index >= Count)
            {
                Debug.LogWarning("index out of range, please check datas.count");
                return new KeyValuePair<string, Object>();
            }
            int _index = 0;
            using (var e = datas.GetEnumerator())
            {
                while (e.MoveNext())
                {
                    if (_index == index)
                    {
                        return e.Current;
                    }
                    ++_index;
                }
            }
            Debug.LogWarning("index out of range, please check datas.count");
            return new KeyValuePair<string, Object>();
        }
  1. 设计迭代器,用户自主选择for或者foreach或enumerator的迭代方式
新建迭代类
public class GameDataEnumerator: IEnumerator<KeyValuePair<string, Object>>, System.IDisposable
    {
        private Dictionary<string, Object> dockers;
        private int enumeratorIndex;

        public KeyValuePair<string, object> Current => ElementAt(enumeratorIndex);

        object IEnumerator.Current => throw new NotImplementedException();

        private GameDataEnumerator()
        {

        }

        public GameDataEnumerator(Dictionary<string, Object> _dockers)
        {
            enumeratorIndex = -1;
            this.dockers = _dockers;
        }

        private KeyValuePair<string, Object> ElementAt(int index)
        {
            if (index < 0)
            {
                Debug.LogError("index must be greater than zero!!!");
                return new KeyValuePair<string, Object>();
            }
            if (index >= dockers.Count)
            {
                Debug.LogError("index out of range");
                return new KeyValuePair<string, Object>();
            }
            int _tempIndex = 0;
            using (var e = dockers.GetEnumerator())
            {
                while (e.MoveNext())
                {
                    if (_tempIndex == index)
                    {
                        return e.Current;
                    }
                    ++_tempIndex;
                }
            }
            return new KeyValuePair<string, Object>();
        }

        public bool MoveNext()
        {
            ++enumeratorIndex;
            return enumeratorIndex < dockers.Count;
        }

        public void Reset()
        {
            enumeratorIndex = -1;
        }

        public void Dispose()
        {
        }
    }
获取迭代器
        public GameDataEnumerator GetEnumerator()
        {
            return new GameDataEnumerator(datas);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            throw new NotImplementedException();
        }
  1. 公用接口
判断是否用有此键
        public bool HaveKey(string key)
        {
            return datas.ContainsKey(key);
        }
判断是否拥有此值
        public bool HaveValue(Object value)
        {
            return datas.ContainsValue(value);
        }
重写equal方法
        public override bool Equals(object obj)
        {
            try
            {
                GameData _compareData = (GameData)obj;
                bool isCountEqual = _compareData.Count == this.Count;
                bool isKeysEqual = _compareData.Keys.Equals(this.Keys);
                bool isValuesEqual = _compareData.Values.Equals(this.Values);
                return isCountEqual && isKeysEqual && isValuesEqual;
            }
            catch
            {
                return false;
            }
        }
  1. 公共属性
获取所有键
        public List<string> Keys { get => GetKeys(); }
		
        private List<string> GetKeys()
        {
            List<string> keys = new List<string>(datas.Count);
            using (var e = datas.GetEnumerator())
            {
                while (e.MoveNext())
                {
                    keys.Add(e.Current.Key);
                }
            }
            return keys;
        }
获取所有值
        public List<Object> Values { get => GetValues(); }

        private List<Object> GetValues()
        {
            List<Object> values = new List<Object>(datas.Count);
            using (var e = datas.GetEnumerator())
            {
                while (e.MoveNext())
                {
                    values.Add(e.Current.Key);
                }
            }
            return values;
        }
获取json字符串
        public String Json { get => this.ToString(); }

        public override string ToString()
        {
            return string.Empty;//可以使用json相关的转换工具或者自定义json,转为字符串
        }

展示代码

点击查看代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using Object = System.Object;

namespace JunFramework
{
    public class GameData :IEnumerable, IDisposable
    {
        private Dictionary<string, Object> datas;

        public int Count { get => datas.Count; }
        public List<string> Keys { get => GetKeys(); }
        public List<Object> Values { get => GetValues(); }
        public String Json { get => this.ToString(); }


        public GameData()
        {
            datas = new Dictionary<string, Object>();
        }

        public Object this[string key]
        {
            get
            {
                Object result = null;
                datas.TryGetValue(key, out result);
                return result;
            }

            set
            {
                if (datas.ContainsKey(key))
                {
                    datas[key] = value;
                }
                else
                {
                    Add(key, value);
                }
            }
        }

        public KeyValuePair<string, Object> this[int index]
        {
            get => ElementAt(index);
        }

        public void Add(string key, Object value)
        {
            datas.Add(key, value);
        }

        private KeyValuePair<string, Object> ElementAt(int index)
        {
            if (index < 0)
            {
                Debug.LogWarning("index must be greater than zero!!!");
                return new KeyValuePair<string, Object>();
            }
            if (index >= Count)
            {
                Debug.LogWarning("index out of range, please check datas.count");
                return new KeyValuePair<string, Object>();
            }
            int _index = 0;
            using (var e = datas.GetEnumerator())
            {
                while (e.MoveNext())
                {
                    if (_index == index)
                    {
                        return e.Current;
                    }
                    ++_index;
                }
            }
            Debug.LogWarning("index out of range, please check datas.count");
            return new KeyValuePair<string, Object>();
        }

        public bool HaveKey(string key)
        {
            return datas.ContainsKey(key);
        }

        public bool HaveValue(Object value)
        {
            return datas.ContainsValue(value);
        }

        public void Clear()
        {
            datas.Clear();
        }

        public void Dispose()
        {
            Clear();
            datas = null;
        }

        public override string ToString()
        {
            return string.Empty;//可以使用json相关的转换工具或者自定义json,转为字符串
        }

        public override bool Equals(object obj)
        {
            try
            {
                GameData _compareData = (GameData)obj;
                bool isCountEqual = _compareData.Count == this.Count;
                bool isKeysEqual = _compareData.Keys.Equals(this.Keys);
                bool isValuesEqual = _compareData.Values.Equals(this.Values);
                return isCountEqual && isKeysEqual && isValuesEqual;
            }
            catch
            {
                return false;
            }
        }

        private List<string> GetKeys()
        {
            List<string> keys = new List<string>(datas.Count);
            using (var e = datas.GetEnumerator())
            {
                while (e.MoveNext())
                {
                    keys.Add(e.Current.Key);
                }
            }
            return keys;
        }

        private List<Object> GetValues()
        {
            List<Object> values = new List<Object>(datas.Count);
            using (var e = datas.GetEnumerator())
            {
                while (e.MoveNext())
                {
                    values.Add(e.Current.Key);
                }
            }
            return values;
        }

        public GameDataEnumerator GetEnumerator()
        {
            return new GameDataEnumerator(datas);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            throw new NotImplementedException();
        }
    }

    public class GameDataEnumerator: IEnumerator<KeyValuePair<string, Object>>, System.IDisposable
    {
        private Dictionary<string, Object> dockers;
        private int enumeratorIndex;

        public KeyValuePair<string, object> Current => ElementAt(enumeratorIndex);

        object IEnumerator.Current => throw new NotImplementedException();

        private GameDataEnumerator()
        {

        }

        public GameDataEnumerator(Dictionary<string, Object> _dockers)
        {
            enumeratorIndex = -1;
            this.dockers = _dockers;
        }

        private KeyValuePair<string, Object> ElementAt(int index)
        {
            if (index >= dockers.Count)
            {
                Debug.LogError("index out of range");
                return new KeyValuePair<string, Object>();
            }
            int _tempIndex = 0;
            using (var e = dockers.GetEnumerator())
            {
                while (e.MoveNext())
                {
                    if (_tempIndex == index)
                    {
                        return e.Current;
                    }
                    ++_tempIndex;
                }
            }
            return new KeyValuePair<string, Object>();
        }

        public bool MoveNext()
        {
            ++enumeratorIndex;
            return enumeratorIndex < dockers.Count;
        }

        public void Reset()
        {
            enumeratorIndex = -1;
        }

        public void Dispose()
        {
        }
    }
}

测试用例

测试工具:Unity2018VisualStudio2017

创建对象
        GameData data = new GameData()
        {
            {"id", 1},
            {"name", "junjiang"},
            {"identity", "student"},
            {"doAction", new System.Action(DoAction)}
        };
        data["id"] = 2;
修改值
        data["id"] = 2;
        data["whatever"] = 0.77f;
查看结果
        Debug.Log(data["id"]);
        Debug.Log(data["name"]);
        Debug.Log(data["identity"]);
        Debug.Log(data[0].Key);
        Debug.Log(data[0].Value.ToString());
        Debug.Log(data["whatever"]);
for循环
        for (int i = 0; i < data.Count; i++)
        {
            try
            {
                System.Action act = (System.Action)data[i].Value;
                act?.Invoke();
            }
            catch
            {
            }
        }
foreach循环
        foreach (var item in data)
        {
            try
            {
                System.Action act = (System.Action)item.Value;
                act?.Invoke();
            }
            catch
            {

            }
        }
enumerator循环
        using (var e = data.GetEnumerator())
        {
            while (e.MoveNext())
            {
                try
                {
                    System.Action act = (System.Action)e.Current.Value;
                    act?.Invoke();
                }
                catch
                {

                }
            }
        }

查看打印结果

image

使用场景

设计思路来源于LiteJson的JsonData,设计此最初目的是满足我的游戏框架,各个对象之间传递数据。添加Json属性是为了方便我保存玩家数据,当然也可以设计成XML属性或者Lua属性皆可。当然此数据结构暂时只跑过部分测试案例,我将在开发我的JunFramework框架中不断检验和完善我的GameData。欢迎大家指正和建议

posted @ 2023-03-05 17:54  军酱不是酱  阅读(179)  评论(0编辑  收藏  举报