天天看點

GameFrameWork學習筆記(一)

目錄

    • 寫在前面的一些廢話
    • 一、架構基礎抽象類
    • 二、架構入口
    • 三、引用池和非托管記憶體

寫在前面的一些廢話

       最近項目核心子產品要重構,負責非核心子產品的我就清閑了不少,想給自己充充電,唔,好像也不算充電,反正就學習一點進階的東西,研究一下架構的相關内容。

       跑到GitHub上找unity相關的架構,star最多的就是entitas。但是上周已經大概研究過unity的DOTS了,大緻了解了ECS的思路,entitas等放假再看吧(先給自己立個flag)。然後就是EllanJiang大佬的GameFramework(在GameFramework的基礎上,大佬還封裝了個UnityGameFramework)。

       對着源碼看了半天感覺有點懵,不懂子產品為什麼這樣寫。就去找文檔和教程(EllanJiang大佬自己寫的文檔隻有兩三篇orz)。然後就發現了這些資料:

       笨木頭GameFramework系列

       煙雨迷離半世殇GameFramework系列

       自然妙有貓仙人GameFramework系列

這三位的教程各有側重,主要看的是貓仙人的,然鵝,貓大佬沒寫完就鴿了!!!是以後面部分就隻好自己分析。寫這個就算是學習筆記,記錄一下(PS:GameFramework版本是2021年一月初的版本)。

       首先,從整體上看GameFramework是Manager of Managers式的。1 是以先來看Managers(就算先看程式入口,還是得拐回來)。

一、架構基礎抽象類

       在Base檔案夾下有個叫 GameFrameworkModule 的類。在工程裡可以看到所有的Manager都是繼承自這個類。

GameFrameWork學習筆記(一)
namespace GameFramework
{
    /// <summary>
    /// 遊戲架構子產品抽象類。
    /// </summary>
    internal abstract class GameFrameworkModule
    {
        /// <summary>
        /// 擷取遊戲架構子產品優先級。
        /// </summary>
        /// <remarks>優先級較高的子產品會優先輪詢,并且關閉操作會後進行。</remarks>
        internal virtual int Priority
        {
            get
            {
                return 0;
            }
        }

        /// <summary>
        /// 遊戲架構子產品輪詢。
        /// </summary>
        /// <param name="elapseSeconds">邏輯流逝時間,以秒為機關。</param>
        /// <param name="realElapseSeconds">真實流逝時間,以秒為機關。</param>
        internal abstract void Update(float elapseSeconds, float realElapseSeconds);

        /// <summary>
        /// 關閉并清理遊戲架構子產品。
        /// </summary>
        internal abstract void Shutdown();
    }
}
           

       從邏輯上看,這個基類比較簡單,隻定義了子產品的輪詢方法,輪詢的優先級以及關閉子產品的方法。文法上簡單說一下internal。internal關鍵字使得它所修飾的類、方法或者屬性等隻能在同一個程式集(dll或者exe)中被通路。2 插一句題外話,現在unity有了程式集定義檔案 Assembly Definition File來代替dll,據說可以加快編譯時間。

       每個子產品的Manager除了繼承這個基類以外,還會實作一個Manager接口。我的了解是:GameFrameworkModule這個類是各個Manager的更高層次的抽象,負責實作它們共有的輪詢和關閉方法,用internal修飾;而接口則是聲明了這個Manager具體的功能,同時外部利用這個接口來通路Manager。

       好了,架構最最基礎的部分就是這些了。接下來去看程式入口。

二、架構入口

namespace GameFramework
{
    /// <summary>
    /// 遊戲架構入口。
    /// </summary>
    public static class GameFrameworkEntry
    {
        private static readonly GameFrameworkLinkedList<GameFrameworkModule> s_GameFrameworkModules = new GameFrameworkLinkedList<GameFrameworkModule>();

        /// <summary>
        /// 所有遊戲架構子產品輪詢。
        /// </summary>
        /// <param name="elapseSeconds">邏輯流逝時間,以秒為機關。</param>
        /// <param name="realElapseSeconds">真實流逝時間,以秒為機關。</param>
        public static void Update(float elapseSeconds, float realElapseSeconds)
        {
            foreach (GameFrameworkModule module in s_GameFrameworkModules)
            {
                module.Update(elapseSeconds, realElapseSeconds);
            }
        }

        /// <summary>
        /// 關閉并清理所有遊戲架構子產品。
        /// </summary>
        public static void Shutdown()
        {
            for (LinkedListNode<GameFrameworkModule> current = s_GameFrameworkModules.Last; current != null; current = current.Previous)
            {
                current.Value.Shutdown();
            }

            s_GameFrameworkModules.Clear();
            ReferencePool.ClearAll();
            Utility.Marshal.FreeCachedHGlobal();
            GameFrameworkLog.SetLogHelper(null);
        }

        /// <summary>
        /// 擷取遊戲架構子產品。
        /// </summary>
        /// <typeparam name="T">要擷取的遊戲架構子產品類型。</typeparam>
        /// <returns>要擷取的遊戲架構子產品。</returns>
        /// <remarks>如果要擷取的遊戲架構子產品不存在,則自動建立該遊戲架構子產品。</remarks>
        public static T GetModule<T>() where T : class
        {
            Type interfaceType = typeof(T);
            if (!interfaceType.IsInterface)
            {
                throw new GameFrameworkException(Utility.Text.Format("You must get module by interface, but '{0}' is not.", interfaceType.FullName));
            }

            if (!interfaceType.FullName.StartsWith("GameFramework.", StringComparison.Ordinal))
            {
                throw new GameFrameworkException(Utility.Text.Format("You must get a Game Framework module, but '{0}' is not.", interfaceType.FullName));
            }

            string moduleName = Utility.Text.Format("{0}.{1}", interfaceType.Namespace, interfaceType.Name.Substring(1));
            Type moduleType = Type.GetType(moduleName);
            if (moduleType == null)
            {
                throw new GameFrameworkException(Utility.Text.Format("Can not find Game Framework module type '{0}'.", moduleName));
            }

            return GetModule(moduleType) as T;
        }

        /// <summary>
        /// 擷取遊戲架構子產品。
        /// </summary>
        /// <param name="moduleType">要擷取的遊戲架構子產品類型。</param>
        /// <returns>要擷取的遊戲架構子產品。</returns>
        /// <remarks>如果要擷取的遊戲架構子產品不存在,則自動建立該遊戲架構子產品。</remarks>
        private static GameFrameworkModule GetModule(Type moduleType)
        {
            foreach (GameFrameworkModule module in s_GameFrameworkModules)
            {
                if (module.GetType() == moduleType)
                {
                    return module;
                }
            }

            return CreateModule(moduleType);
        }

        /// <summary>
        /// 建立遊戲架構子產品。
        /// </summary>
        /// <param name="moduleType">要建立的遊戲架構子產品類型。</param>
        /// <returns>要建立的遊戲架構子產品。</returns>
        private static GameFrameworkModule CreateModule(Type moduleType)
        {
            GameFrameworkModule module = (GameFrameworkModule)Activator.CreateInstance(moduleType);
            if (module == null)
            {
                throw new GameFrameworkException(Utility.Text.Format("Can not create module '{0}'.", moduleType.FullName));
            }

            LinkedListNode<GameFrameworkModule> current = s_GameFrameworkModules.First;
            while (current != null)
            {
                if (module.Priority > current.Value.Priority)
                {
                    break;
                }

                current = current.Next;
            }

            if (current != null)
            {
                s_GameFrameworkModules.AddBefore(current, module);
            }
            else
            {
                s_GameFrameworkModules.AddLast(module);
            }

            return module;
        }
    }
}
           

       這個類說是架構的入口,實質上就是the MANAGER of managers。先看屬性,就聲明了一個List:GameFrameworkLinkedList s_GameFrameworkModules(GameFrameworkLinkedList實際上就是一個内置了對象池的LinkedList),用來存儲用到的所有Manager。方法裡看到了兩個老熟人:Update() 和 Shutdown() 。在這兩個方法裡周遊調用了各個Manager的Update() 和 Shutdown()。剩下的就是建立和擷取Manager的方法,在建立時會根據Manager的Priority進行排序。

       提一下CreateModule()裡用到的**Activator.CreateInstance()**方法。這個方法可以通過傳入一個類名,來擷取該類的一個執行個體,通常會結合反射使用,也可以向上面一樣在類工廠裡動态建立本地類型,不用說這個方法會比較影響性能。

三、引用池和非托管記憶體

       從GameFrameworkEntry的ShutDown()裡可以看到,除了關閉各個子產品以外還進行了引用池和非托管記憶體的釋放:ReferencePool.ClearAll();和Utility.Marshal.FreeCachedHGlobal();

public static partial class ReferencePool
    {
        private sealed class ReferenceCollection
        {
            private readonly Queue<IReference> m_References;
            private readonly Type m_ReferenceType;
            private int m_UsingReferenceCount;
            private int m_AcquireReferenceCount;
            private int m_ReleaseReferenceCount;
            private int m_AddReferenceCount;
            private int m_RemoveReferenceCount;

            public ReferenceCollection(Type referenceType)
            {
                m_References = new Queue<IReference>(); 
                m_ReferenceType = referenceType;
                m_UsingReferenceCount = 0;
                m_AcquireReferenceCount = 0;
                m_ReleaseReferenceCount = 0;
                m_AddReferenceCount = 0;
                m_RemoveReferenceCount = 0;
            }

            public Type ReferenceType
            {
                get
                {
                    return m_ReferenceType;
                }
            }

            public int UnusedReferenceCount
            {
                get
                {
                    return m_References.Count;
                }
            }

            public int UsingReferenceCount
            {
                get
                {
                    return m_UsingReferenceCount;
                }
            }

            public int AcquireReferenceCount
            {
                get
                {
                    return m_AcquireReferenceCount;
                }
            }

            public int ReleaseReferenceCount
            {
                get
                {
                    return m_ReleaseReferenceCount;
                }
            }

            public int AddReferenceCount
            {
                get
                {
                    return m_AddReferenceCount;
                }
            }

            public int RemoveReferenceCount
            {
                get
                {
                    return m_RemoveReferenceCount;
                }
            }

            public T Acquire<T>() where T : class, IReference, new()
            {
                if (typeof(T) != m_ReferenceType)
                {
                    throw new GameFrameworkException("Type is invalid.");
                }

                m_UsingReferenceCount++;
                m_AcquireReferenceCount++;
                lock (m_References)
                {
                    if (m_References.Count > 0)
                    {
                        return (T)m_References.Dequeue();
                    }
                }

                m_AddReferenceCount++;
                return new T();
            }

            public IReference Acquire()
            {
                m_UsingReferenceCount++;
                m_AcquireReferenceCount++;
                lock (m_References)
                {
                    if (m_References.Count > 0)
                    {
                        return m_References.Dequeue();
                    }
                }

                m_AddReferenceCount++;
                return (IReference)Activator.CreateInstance(m_ReferenceType);
            }

            public void Release(IReference reference)
            {
                reference.Clear();
                lock (m_References)
                {
                    if (m_EnableStrictCheck && m_References.Contains(reference))
                    {
                        throw new GameFrameworkException("The reference has been released.");
                    }

                    m_References.Enqueue(reference);
                }

                m_ReleaseReferenceCount++;
                m_UsingReferenceCount--;
            }

            public void Add<T>(int count) where T : class, IReference, new()
            {
                if (typeof(T) != m_ReferenceType)
                {
                    throw new GameFrameworkException("Type is invalid.");
                }

                lock (m_References)
                {
                    m_AddReferenceCount += count;
                    while (count-- > 0)
                    {
                        m_References.Enqueue(new T());
                    }
                }
            }

            public void Add(int count)
            {
                lock (m_References)
                {
                    m_AddReferenceCount += count;
                    while (count-- > 0)
                    {
                        m_References.Enqueue((IReference)Activator.CreateInstance(m_ReferenceType));
                    }
                }
            }

            public void Remove(int count)
            {
                lock (m_References)
                {
                    if (count > m_References.Count)
                    {
                        count = m_References.Count;
                    }

                    m_RemoveReferenceCount += count;
                    while (count-- > 0)
                    {
                        m_References.Dequeue();
                    }
                }
            }

            public void RemoveAll()
            {
                lock (m_References)
                {
                    m_RemoveReferenceCount += m_References.Count;
                    m_References.Clear();
                }
            }
        }
    }
           

       作者在引用池裡定義了一個私有類:ReferenceCollection,這個類隻是定義了很基礎的添加、移除、擷取和釋放方法。另外,看這個代碼總感覺訓示釋放和移除數量這倆變量沒什麼用,果然大佬思考問題的還是比較全面的,暫時不是很了解。(找到答案回來補上,這兩個變量在editor模式下運作時,可以在inspector裡看到)

public static partial class ReferencePool
    {
        private static readonly Dictionary<Type, ReferenceCollection> s_ReferenceCollections = new Dictionary<Type, ReferenceCollection>();
        private static bool m_EnableStrictCheck = false;

        /// <summary>
        /// 擷取或設定是否開啟強制檢查。
        /// </summary>
        public static bool EnableStrictCheck
        {
            get
            {
                return m_EnableStrictCheck;
            }
            set
            {
                m_EnableStrictCheck = value;
            }
        }

        /// <summary>
        /// 擷取引用池的數量。
        /// </summary>
        public static int Count
        {
            get
            {
                return s_ReferenceCollections.Count;
            }
        }

        /// <summary>
        /// 擷取所有引用池的資訊。
        /// </summary>
        /// <returns>所有引用池的資訊。</returns>
        public static ReferencePoolInfo[] GetAllReferencePoolInfos()
        {
            int index = 0;
            ReferencePoolInfo[] results = null;

            lock (s_ReferenceCollections)
            {
                results = new ReferencePoolInfo[s_ReferenceCollections.Count];
                foreach (KeyValuePair<Type, ReferenceCollection> referenceCollection in s_ReferenceCollections)
                {
                    results[index++] = new ReferencePoolInfo(referenceCollection.Key, referenceCollection.Value.UnusedReferenceCount, referenceCollection.Value.UsingReferenceCount, referenceCollection.Value.AcquireReferenceCount, referenceCollection.Value.ReleaseReferenceCount, referenceCollection.Value.AddReferenceCount, referenceCollection.Value.RemoveReferenceCount);
                }
            }

            return results;
        }

        /// <summary>
        /// 清除所有引用池。
        /// </summary>
        public static void ClearAll()
        {
            lock (s_ReferenceCollections)
            {
                foreach (KeyValuePair<Type, ReferenceCollection> referenceCollection in s_ReferenceCollections)
                {
                    referenceCollection.Value.RemoveAll();
                }

                s_ReferenceCollections.Clear();
            }
        }

        /// <summary>
        /// 從引用池擷取引用。
        /// </summary>
        /// <typeparam name="T">引用類型。</typeparam>
        /// <returns>引用。</returns>
        public static T Acquire<T>() where T : class, IReference, new()
        {
            return GetReferenceCollection(typeof(T)).Acquire<T>();
        }

        /// <summary>
        /// 從引用池擷取引用。
        /// </summary>
        /// <param name="referenceType">引用類型。</param>
        /// <returns>引用。</returns>
        public static IReference Acquire(Type referenceType)
        {
            InternalCheckReferenceType(referenceType);
            return GetReferenceCollection(referenceType).Acquire();
        }

        /// <summary>
        /// 将引用歸還引用池。
        /// </summary>
        /// <param name="reference">引用。</param>
        public static void Release(IReference reference)
        {
            if (reference == null)
            {
                throw new GameFrameworkException("Reference is invalid.");
            }

            Type referenceType = reference.GetType();
            InternalCheckReferenceType(referenceType);
            GetReferenceCollection(referenceType).Release(reference);
        }

        /// <summary>
        /// 向引用池中追加指定數量的引用。
        /// </summary>
        /// <typeparam name="T">引用類型。</typeparam>
        /// <param name="count">追加數量。</param>
        public static void Add<T>(int count) where T : class, IReference, new()
        {
            GetReferenceCollection(typeof(T)).Add<T>(count);
        }

        /// <summary>
        /// 向引用池中追加指定數量的引用。
        /// </summary>
        /// <param name="referenceType">引用類型。</param>
        /// <param name="count">追加數量。</param>
        public static void Add(Type referenceType, int count)
        {
            InternalCheckReferenceType(referenceType);
            GetReferenceCollection(referenceType).Add(count);
        }

        /// <summary>
        /// 從引用池中移除指定數量的引用。
        /// </summary>
        /// <typeparam name="T">引用類型。</typeparam>
        /// <param name="count">移除數量。</param>
        public static void Remove<T>(int count) where T : class, IReference
        {
            GetReferenceCollection(typeof(T)).Remove(count);
        }

        /// <summary>
        /// 從引用池中移除指定數量的引用。
        /// </summary>
        /// <param name="referenceType">引用類型。</param>
        /// <param name="count">移除數量。</param>
        public static void Remove(Type referenceType, int count)
        {
            InternalCheckReferenceType(referenceType);
            GetReferenceCollection(referenceType).Remove(count);
        }

        /// <summary>
        /// 從引用池中移除所有的引用。
        /// </summary>
        /// <typeparam name="T">引用類型。</typeparam>
        public static void RemoveAll<T>() where T : class, IReference
        {
            GetReferenceCollection(typeof(T)).RemoveAll();
        }

        /// <summary>
        /// 從引用池中移除所有的引用。
        /// </summary>
        /// <param name="referenceType">引用類型。</param>
        public static void RemoveAll(Type referenceType)
        {
            InternalCheckReferenceType(referenceType);
            GetReferenceCollection(referenceType).RemoveAll();
        }

        private static void InternalCheckReferenceType(Type referenceType)
        {
            if (!m_EnableStrictCheck)
            {
                return;
            }

            if (referenceType == null)
            {
                throw new GameFrameworkException("Reference type is invalid.");
            }

            if (!referenceType.IsClass || referenceType.IsAbstract)
            {
                throw new GameFrameworkException("Reference type is not a non-abstract class type.");
            }

            if (!typeof(IReference).IsAssignableFrom(referenceType))
            {
                throw new GameFrameworkException(Utility.Text.Format("Reference type '{0}' is invalid.", referenceType.FullName));
            }
        }

        private static ReferenceCollection GetReferenceCollection(Type referenceType)
        {
            if (referenceType == null)
            {
                throw new GameFrameworkException("ReferenceType is invalid.");
            }

            ReferenceCollection referenceCollection = null;
            lock (s_ReferenceCollections)
            {
                if (!s_ReferenceCollections.TryGetValue(referenceType, out referenceCollection))
                {
                    referenceCollection = new ReferenceCollection(referenceType);
                    s_ReferenceCollections.Add(referenceType, referenceCollection);
                }
            }

            return referenceCollection;
        }
    }
           

       這部分概括來說就是引用池的管理類。除了增加了一個強制檢查的方法外,就是對ReferenceCollection做了簡單封裝。但是,作為一個基礎類這個引用池用到的地方非常多。

       下面這個類也是一個通用的基礎類,從功能上來講就是把對象和資料流進行互相轉換,隻不過重載比較多。

public static partial class Utility
    {
        /// <summary>
        /// Marshal 相關的實用函數。
        /// </summary>
        public static class Marshal
        {
            private const int BlockSize = 1024 * 4;
            private static IntPtr s_CachedHGlobalPtr = IntPtr.Zero;
            private static int s_CachedHGlobalSize = 0;

            /// <summary>
            /// 擷取緩存的從程序的非托管記憶體中配置設定的記憶體的大小。
            /// </summary>
            public static int CachedHGlobalSize
            {
                get
                {
                    return s_CachedHGlobalSize;
                }
            }

            /// <summary>
            /// 確定從程序的非托管記憶體中配置設定足夠大小的記憶體并緩存。
            /// </summary>
            /// <param name="ensureSize">要確定從程序的非托管記憶體中配置設定記憶體的大小。</param>
            public static void EnsureCachedHGlobalSize(int ensureSize)
            {
                if (ensureSize < 0)
                {
                    throw new GameFrameworkException("Ensure size is invalid.");
                }

                if (s_CachedHGlobalPtr == IntPtr.Zero || s_CachedHGlobalSize < ensureSize)
                {
                    FreeCachedHGlobal();
                    int size = (ensureSize - 1 + BlockSize) / BlockSize * BlockSize;
                    s_CachedHGlobalPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(size);
                    s_CachedHGlobalSize = size;
                }
            }

            /// <summary>
            /// 釋放緩存的從程序的非托管記憶體中配置設定的記憶體。
            /// </summary>
            public static void FreeCachedHGlobal()
            {
                if (s_CachedHGlobalPtr != IntPtr.Zero)
                {
                    System.Runtime.InteropServices.Marshal.FreeHGlobal(s_CachedHGlobalPtr);
                    s_CachedHGlobalPtr = IntPtr.Zero;
                    s_CachedHGlobalSize = 0;
                }
            }

            /// <summary>
            /// 将資料從對象轉換為二進制流。
            /// </summary>
            /// <typeparam name="T">要轉換的對象的類型。</typeparam>
            /// <param name="structure">要轉換的對象。</param>
            /// <returns>存儲轉換結果的二進制流。</returns>
            public static byte[] StructureToBytes<T>(T structure)
            {
                return StructureToBytes(structure, System.Runtime.InteropServices.Marshal.SizeOf(typeof(T)));
            }

            /// <summary>
            /// 将資料從對象轉換為二進制流。
            /// </summary>
            /// <typeparam name="T">要轉換的對象的類型。</typeparam>
            /// <param name="structure">要轉換的對象。</param>
            /// <param name="structureSize">要轉換的對象的大小。</param>
            /// <returns>存儲轉換結果的二進制流。</returns>
            internal static byte[] StructureToBytes<T>(T structure, int structureSize)
            {
                if (structureSize < 0)
                {
                    throw new GameFrameworkException("Structure size is invalid.");
                }

                EnsureCachedHGlobalSize(structureSize);
                System.Runtime.InteropServices.Marshal.StructureToPtr(structure, s_CachedHGlobalPtr, true);
                byte[] result = new byte[structureSize];
                System.Runtime.InteropServices.Marshal.Copy(s_CachedHGlobalPtr, result, 0, structureSize);
                return result;
            }

            /// <summary>
            /// 将資料從對象轉換為二進制流。
            /// </summary>
            /// <typeparam name="T">要轉換的對象的類型。</typeparam>
            /// <param name="structure">要轉換的對象。</param>
            /// <param name="result">存儲轉換結果的二進制流。</param>
            public static void StructureToBytes<T>(T structure, byte[] result)
            {
                StructureToBytes(structure, System.Runtime.InteropServices.Marshal.SizeOf(typeof(T)), result, 0);
            }

            /// <summary>
            /// 将資料從對象轉換為二進制流。
            /// </summary>
            /// <typeparam name="T">要轉換的對象的類型。</typeparam>
            /// <param name="structure">要轉換的對象。</param>
            /// <param name="structureSize">要轉換的對象的大小。</param>
            /// <param name="result">存儲轉換結果的二進制流。</param>
            internal static void StructureToBytes<T>(T structure, int structureSize, byte[] result)
            {
                StructureToBytes(structure, structureSize, result, 0);
            }

            /// <summary>
            /// 将資料從對象轉換為二進制流。
            /// </summary>
            /// <typeparam name="T">要轉換的對象的類型。</typeparam>
            /// <param name="structure">要轉換的對象。</param>
            /// <param name="result">存儲轉換結果的二進制流。</param>
            /// <param name="startIndex">寫入存儲轉換結果的二進制流的起始位置。</param>
            public static void StructureToBytes<T>(T structure, byte[] result, int startIndex)
            {
                StructureToBytes(structure, System.Runtime.InteropServices.Marshal.SizeOf(typeof(T)), result, startIndex);
            }

            /// <summary>
            /// 将資料從對象轉換為二進制流。
            /// </summary>
            /// <typeparam name="T">要轉換的對象的類型。</typeparam>
            /// <param name="structure">要轉換的對象。</param>
            /// <param name="structureSize">要轉換的對象的大小。</param>
            /// <param name="result">存儲轉換結果的二進制流。</param>
            /// <param name="startIndex">寫入存儲轉換結果的二進制流的起始位置。</param>
            internal static void StructureToBytes<T>(T structure, int structureSize, byte[] result, int startIndex)
            {
                if (structureSize < 0)
                {
                    throw new GameFrameworkException("Structure size is invalid.");
                }

                if (result == null)
                {
                    throw new GameFrameworkException("Result is invalid.");
                }

                if (startIndex < 0)
                {
                    throw new GameFrameworkException("Start index is invalid.");
                }

                if (startIndex + structureSize > result.Length)
                {
                    throw new GameFrameworkException("Result length is not enough.");
                }

                EnsureCachedHGlobalSize(structureSize);
                System.Runtime.InteropServices.Marshal.StructureToPtr(structure, s_CachedHGlobalPtr, true);
                System.Runtime.InteropServices.Marshal.Copy(s_CachedHGlobalPtr, result, startIndex, structureSize);
            }

            /// <summary>
            /// 将資料從二進制流轉換為對象。
            /// </summary>
            /// <typeparam name="T">要轉換的對象的類型。</typeparam>
            /// <param name="buffer">要轉換的二進制流。</param>
            /// <returns>存儲轉換結果的對象。</returns>
            public static T BytesToStructure<T>(byte[] buffer)
            {
                return BytesToStructure<T>(System.Runtime.InteropServices.Marshal.SizeOf(typeof(T)), buffer, 0);
            }

            /// <summary>
            /// 将資料從二進制流轉換為對象。
            /// </summary>
            /// <typeparam name="T">要轉換的對象的類型。</typeparam>
            /// <param name="buffer">要轉換的二進制流。</param>
            /// <param name="startIndex">讀取要轉換的二進制流的起始位置。</param>
            /// <returns>存儲轉換結果的對象。</returns>
            public static T BytesToStructure<T>(byte[] buffer, int startIndex)
            {
                return BytesToStructure<T>(System.Runtime.InteropServices.Marshal.SizeOf(typeof(T)), buffer, startIndex);
            }

            /// <summary>
            /// 将資料從二進制流轉換為對象。
            /// </summary>
            /// <typeparam name="T">要轉換的對象的類型。</typeparam>
            /// <param name="structureSize">要轉換的對象的大小。</param>
            /// <param name="buffer">要轉換的二進制流。</param>
            /// <returns>存儲轉換結果的對象。</returns>
            internal static T BytesToStructure<T>(int structureSize, byte[] buffer)
            {
                return BytesToStructure<T>(structureSize, buffer, 0);
            }

            /// <summary>
            /// 将資料從二進制流轉換為對象。
            /// </summary>
            /// <typeparam name="T">要轉換的對象的類型。</typeparam>
            /// <param name="structureSize">要轉換的對象的大小。</param>
            /// <param name="buffer">要轉換的二進制流。</param>
            /// <param name="startIndex">讀取要轉換的二進制流的起始位置。</param>
            /// <returns>存儲轉換結果的對象。</returns>
            internal static T BytesToStructure<T>(int structureSize, byte[] buffer, int startIndex)
            {
                if (structureSize < 0)
                {
                    throw new GameFrameworkException("Structure size is invalid.");
                }

                if (buffer == null)
                {
                    throw new GameFrameworkException("Buffer is invalid.");
                }

                if (startIndex < 0)
                {
                    throw new GameFrameworkException("Start index is invalid.");
                }

                if (startIndex + structureSize > buffer.Length)
                {
                    throw new GameFrameworkException("Buffer length is not enough.");
                }

                EnsureCachedHGlobalSize(structureSize);
                System.Runtime.InteropServices.Marshal.Copy(buffer, startIndex, s_CachedHGlobalPtr, structureSize);
                return (T)System.Runtime.InteropServices.Marshal.PtrToStructure(s_CachedHGlobalPtr, typeof(T));
            }
        }
    }
           

       對我來說,非托管的代碼不怎麼用到,甚至都不常見到。好在這個類裡使用的api比較少,學習起來不費勁,趁這個機會記錄下。首先是IntPtr。官方文檔說是 A platform-specific type that is used to represent a pointer or a handle. 好吧,姑且可以認為是個指針。在Stack Overflow上有看到說 In general (across programming languages), a pointer is a number that represents a physical location in memory. A null pointer is (almost always) one that points to 0, and is widely recognized as “not pointing to anything”. Since systems have different amounts of supported memory, it doesn’t always take the same number of bytes to hold that number, so we call a “native size integer” one that can hold a pointer on any particular system. 那簡單來說IntPtr就是一個會根據本機記憶體位址大小而改變的有範圍整數。另外還看到這麼個說法 A pointer is something that points to an address in memory. In managed languages you have references (the address can move around) while in unmanaged languages you have pointers (the address is fixed). Emmmmm,今天才知道這個差別。StructureToPtr() 的第三個參數為true時會釋放掉要轉換的對象指向的所有子結構。文檔上是這麼說的:

GameFrameWork學習筆記(一)

       另外,這裡有個記憶體洩漏的案例。

       Copy() 方法原型重載太多了,直接在文檔看吧

       最後還有個問題不明白,就是在EnsureCachedHGlobalSize() 裡面定義要配置設定的空間大小 int size = (ensureSize - 1 + BlockSize) / BlockSize * BlockSize; 這裡size為什麼要這樣計算?

       概括一下,看了架構的最基礎部分、入口以及涉及到的兩個工具類,除了學習到一些文法、api以外,就隻有一個想法,基類真的好抽象啊!

  1. 引用自貓仙人 ↩︎
  2. 直接看文檔吧.(記一下,如果在命名空間直接聲明一個類、結構體或接口或委托并且沒有指明通路性的話,預設為internal。如果嵌套的話預設為private(接口除外)) ↩︎

繼續閱讀