所以我想知道如果在純.NET中有一個答案來表示任意數據類型的集合。我知道那裏有舊的VB6 Collections,但我一直在尋找類似於Generics的東西,但是不必在編譯時指定類型,或者找到一種聰明的方法來讓代碼自行確定類型然後調用一些通用類。VB.NET中的任意類型的集合?
爲什麼?我很無聊,我認爲嘗試和實施我自己的NBT庫,或NamedBinaryTag
會很有趣。這是流行的Minecraft遊戲中使用的存儲格式。規範文檔在這裏:http://www.minecraft.net/docs/NBT.txt
我知道現有的實現,但如果我只是作爲一個學習經驗來更好地掌握文件流,字節數組,末尾形式,那麼沒有必要複製這些實現轉換,和一般的.NET的東西(我曾經很多擺弄VB6/VBA,所以.NET是一個巨大的變化)。
掛我的是TAG_Compound。按照該規範,它基本上是任何其他Tag類型的對象的集合,包括附加嵌套的TAG_Compounds。你可以用這種格式做一些奇怪的嵌套/遞歸。
我已經在我的腦海中做了一個粗略的概述,如何做其他類,但任意類型的存儲只是讓我畫一個空白的如何將它存儲在存根類(clsTagCompound)因此,一個泛型類(clsNBT(Of T))可以使用泛型函數來訪問有效負載。
List(Of T)看起來像它可以工作,如果我可以餵它一個共同的接口。但是由於Generic類將是主要組件,它的接口也是通用的,並且只會導致討厭的泛型鏈(List(Of(clsNBT(Of XXX)))。因爲這個規範適用於字節流,這裏是一個非壓縮NBT文件的樣子(使用Minecraft編輯器創建的)的十六進制輸出,它是一個包裹在TAG_Compound中的TAG_String,雖然沒有具體說明,但它是通常第一個標記在NBT文件中找到,它封裝了所有其他標籤。
0A 00 04 72 6F 6F 74 08 00 06 66 6F 6F 62 61 72 00 07 50 49 52 41 54 45 21 00
從左到右:
字節1:TagType - 指定TAG_Compound。
字節2-3:TAG_Compound名稱的字符串長度。
字節4-7:「root」,TAG_Compound的名稱。
字節8:TagType - 指定TAG_String(嵌入在TAG_Compound中)。
字節9-10:TAG_String名稱的字符串長度。
字節11-16:「foobar」,TAG_String的名稱。
字節17-18:有效負載長度(TAG_String,所以字符串長度)。
字節19-25:「PIRATES!」,TAG_String的有效負載。
字節26:TagType - 指定TAG_End,標記TAG_Compound或TAG_List的結尾。
相同的基本原則適用於其他標籤類型。非常簡單的設計,但看起來非常強大。可能是爲什麼一個alpha級別的代碼運行得很好的原因之一,特別是在Java中。
編輯:這是鏈接到級別規範。它給看到這些標籤是如何協同工作的更容易理解的方式:
http://www.minecraftwiki.net/wiki/Alpha_Level_Format#level.dat_Format
注:我沒有做任何MODS的遊戲太有興趣 - 我只是找到了NBT格式整齊,很簡單,可以可能有用。已經在思考如何擴展格式以處理標記中的無符號類型(即TAG_UInteger),並且可能在幻數前加上未壓縮的流(如Linux/Unix可執行文件在前四個字節中有「ELF」)。這將防止這些工具中的任何問題被用來打開任意/意外的數據格式(我也可能會將這些想法傳遞給遊戲開發人員)。
編輯2:所以我改變了事情。 clsNamedBinaryTag現在是一個抽象類,它實現在通用接口中定義的通用方法:
Friend Interface INbt(Of T)
...
Function GetPayload() As T
Function SetPayload(ByRef data As BinaryReader) As Boolean
End Interface
Friend MustInherit Class clsNamedBinaryTag(Of T)
Implements INbt(Of T)
...
Protected Friend MustOverride _
Function GetPayload() As T _
Implements INbt(Of T).GetPayload
Protected Friend MustOverride _
Function SetPayload(ByRef data As BinaryReader) As Boolean _
Implements INbt(Of T).SetPayload
End Class
GetPayload
是通用的方法,因爲它會提取和返回任意類型的有效載荷。非常適合Strings等簡單的事情。當我們遇到TAG_Compound時不是那麼好。
我在做的事是讓所有派生類實現INbt(Of T)。對於clsTagCompound,它的SetPayload
方法將在解析化合物的名稱字段後開始漫步字節流。對於它遇到的每個新的TagType,它理論上都會調用DirectCast上的Dim變爲INbt(Of T)的臨時變量,將其轉換爲定義該特定TagType的類。
但這似乎並沒有按計劃運作。我相信我的Catch 22甚至使用clsTagCompound,我仍然需要定義T,這就是我再次卡住的地方。我需要創建一個不通用的接口,但可以應用於各種標籤類型的所有類,並仍然調用它們的GetPayload
函數來返回特定標籤的有效載荷。
GetType我以前玩過。類型...對我來說是一個相當新的東西。似乎根據MSDN鏈接找出通用對象中包含的數據類型。但是不是很貴?如果我正確理解術語(並且在發現/使用ILDASM後),則需要對包含的數據類型進行裝箱和拆箱,這需要幾個週期。 – Kumba 2010-10-22 00:30:02
我正在按標籤類,順便說一句。所以現在,我有clsTagString和clsTagCompound。 NBT規範提供了一個由一個TAG_Compound「root」和一個TAG_String「hello world」組成的小型測試用例NBT文件的URL,其值爲「Bananrama」。我的目標是能夠首先解析,然後從那裏開始工作。 clsTagString是完整的,理論上應該可以工作。這是clsTagCompound,這被證明是一個愚蠢的,因爲它作爲其有效載荷任何其他標籤,包括額外的TAG_compounds。泛型也許是答案,但是這些東西很難考慮。 – Kumba 2010-10-22 00:33:49
進一步的(愚蠢的評論限制),我有一個基類,clsNamedBinaryTag,它包含所有標籤通用的TagType,TagName和TagNameLength字段的常見訪問器屬性/函數。標籤也可以沒有名稱 - 可以通過查看TagNameLength是否爲0來確定。 問題在於Payload。每個標籤實現不同的有效負載,所以TAG_String存儲一個以其字符串長度爲前綴的字符串。 TAG_Integer存儲一個32位有符號整數值。 TAG_List存儲(基本上)一個單一類型的數組。 – Kumba 2010-10-22 00:37:31