• <label id="pxtpz"><meter id="pxtpz"></meter></label>
      1. <span id="pxtpz"><optgroup id="pxtpz"></optgroup></span>

        當(dāng)前位置:雨林木風(fēng)下載站 > 技術(shù)開(kāi)發(fā)教程 > 詳細(xì)頁(yè)面

        在 C# 中處理結(jié)構(gòu)內(nèi)的數(shù)組

        在 C# 中處理結(jié)構(gòu)內(nèi)的數(shù)組

        更新時(shí)間:2022-05-01 文章作者:未知 信息來(lái)源:網(wǎng)絡(luò) 閱讀次數(shù):

        在 C/C++ 代碼中,大量摻雜著包括普通類型和數(shù)組的結(jié)構(gòu),如定義 PE 文件頭結(jié)構(gòu)的 IMAGE_OPTIONAL_HEADER 結(jié)構(gòu)定義如下:


        以下內(nèi)容為程序代碼:

        typedef struct _IMAGE_DATA_DIRECTORY {
        DWORD VirtualAddress;
        DWORD Size;
        } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

        #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16

        typedef struct _IMAGE_OPTIONAL_HEADER {

        WORD Magic;

        //...

        DWORD NumberOfRvaAndSizes;
        IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];

        } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;



        在 C/C++ 中這樣在結(jié)構(gòu)中使用數(shù)組是完全正確的,因?yàn)檫@些數(shù)組將作為整個(gè)結(jié)構(gòu)的一部分,在對(duì)結(jié)構(gòu)操作時(shí)直接訪問(wèn)結(jié)構(gòu)所在內(nèi)存塊。但在 C# 這類語(yǔ)言中,則無(wú)法直接如此使用,因?yàn)閿?shù)組是作為一種特殊的引用類型存在的,如定義:
        以下內(nèi)容為程序代碼:

        public struct IMAGE_DATA_DIRECTORY
        {
        public uint VirtualAddress;
        public uint Size;
        }

        public struct IMAGE_OPTIONAL_HEADER
        {
        public const int IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;

        public ushort Magic;

        //...

        public uint NumberOfRvaAndSizes;

        public IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
        }



        在 C# 中這樣定義結(jié)構(gòu)中的數(shù)組是錯(cuò)誤的,會(huì)在編譯時(shí)獲得一個(gè) CS0650 錯(cuò)誤:

        以下為引用:

        error CS0650: 語(yǔ)法錯(cuò)誤,錯(cuò)誤的數(shù)組聲明符。若要聲明托管數(shù)組,秩說(shuō)明符應(yīng)位于變量標(biāo)識(shí)符之前




        如果改用 C# 中引用類型的類似定義語(yǔ)法,如
        以下內(nèi)容為程序代碼:

        public struct IMAGE_OPTIONAL_HEADER
        {
        public const int IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;

        public ushort Magic;

        //...

        public uint NumberOfRvaAndSizes;

        public IMAGE_DATA_DIRECTORY[] DataDirectory = new IMAGE_DATA_DIRECTORY[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
        }



        則得到一個(gè) CS0573 錯(cuò)誤:

        以下為引用:

        error CS0573: “IMAGE_OPTIONAL_HEADER.DataDirectory” : 結(jié)構(gòu)中不能有實(shí)例字段初始值設(shè)定項(xiàng)




        因?yàn)榻Y(jié)構(gòu)內(nèi)是不能夠有引用類型的初始化的,這與 class 的初始化工作不同。如此一來(lái)只能將數(shù)組的初始化放到構(gòu)造函數(shù)中,而且結(jié)構(gòu)還不能有無(wú)參數(shù)的缺省構(gòu)造函數(shù),真是麻煩,呵呵
        以下內(nèi)容為程序代碼:

        public struct IMAGE_OPTIONAL_HEADER
        {
        public const int IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;

        public ushort Magic;

        public uint NumberOfRvaAndSizes;

        public IMAGE_DATA_DIRECTORY[] DataDirectory;

        public IMAGE_OPTIONAL_HEADER(IntPtr ptr)
        {
        Magic = 0;
        NumberOfRvaAndSizes = 0;

        DataDirectory = new IMAGE_DATA_DIRECTORY[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
        }
        }



        這樣一來(lái)看起來(lái)似乎能使了,但如果使用 Marshal.SizeOf(typeof(IMAGE_OPTIONAL_HEADER)) 看看就會(huì)發(fā)現(xiàn),其長(zhǎng)度根本就跟 C/C++ 中定義的長(zhǎng)度不同。問(wèn)題還是在于結(jié)構(gòu)中數(shù)組,雖然看起來(lái)此數(shù)組是定義在結(jié)構(gòu)內(nèi),但實(shí)際上在此結(jié)構(gòu)中只有一個(gè)指向 IMAGE_DATA_DIRECTORY[] 數(shù)組類型的指針而已,本應(yīng)保存在 DataDirectory 未知的數(shù)組內(nèi)容,是在托管堆中。
        于是問(wèn)題就變成如何將引用類型的數(shù)組,放在一個(gè)值類型的結(jié)構(gòu)中。

        解決的方法有很多,如通過(guò) StructLayout 顯式指定結(jié)構(gòu)的長(zhǎng)度來(lái)限定內(nèi)容:
        以下內(nèi)容為程序代碼:

        [StructLayout(LayoutKind.Sequential, Size=XXX)]
        public struct IMAGE_OPTIONAL_HEADER
        {
        public const int IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;

        public ushort Magic;

        public uint NumberOfRvaAndSizes;

        public IMAGE_DATA_DIRECTORY DataDirectory;
        }



        注意這兒 StructLayout 中 Size 指定的是整個(gè)結(jié)構(gòu)的長(zhǎng)度,因?yàn)?DataDirectory 已經(jīng)是最后一個(gè)字段,故而數(shù)組的后 15 個(gè)元素被保存在未命名的堆棧空間內(nèi)。使用的時(shí)候稍微麻煩一點(diǎn),需要一次性讀取整個(gè)結(jié)構(gòu),然后通過(guò) unsafe 代碼的指針操作來(lái)訪問(wèn) DataDirectory 字段后面的其他數(shù)組元素。
        這種方法的優(yōu)點(diǎn)是定義簡(jiǎn)單,但使用時(shí)需要依賴 unsafe 的指針操作代碼,且受到數(shù)組字段必須是在最后的限制。當(dāng)然也可以通過(guò) LayoutKind.Explicit 顯式指定每個(gè)字段的未知來(lái)模擬多個(gè)結(jié)構(gòu)內(nèi)嵌數(shù)組,但這需要手工計(jì)算每個(gè)字段偏移,比較麻煩。

        另外一種解決方法是通過(guò) Marshal 的支持,顯式定義數(shù)組元素所占位置,如
        以下內(nèi)容為程序代碼:

        [StructLayout(LayoutKind.Sequential, Pack=1)]
        public struct IMAGE_OPTIONAL_HEADER
        {
        public const int IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;

        public ushort Magic;

        public uint NumberOfRvaAndSizes;

        [MarshalAs(UnmanagedType.ByValArray, SizeConst=IMAGE_NUMBEROF_DIRECTORY_ENTRIES)]
        public IMAGE_DATA_DIRECTORY[] DataDirectory;
        }



        這種方法相對(duì)來(lái)說(shuō)要優(yōu)雅一些,通過(guò) Marshal 機(jī)制支持的屬性來(lái)定義值數(shù)組語(yǔ)義,使用起來(lái)與普通的數(shù)組區(qū)別不算太大。上述數(shù)組定義被編譯成 IL 定義:
        以下內(nèi)容為程序代碼:

        .field public marshal( fixed array [16]) valuetype IMAGE_DATA_DIRECTORY[] DataDirectory



        雖然類型還是 valuetype IMAGE_DATA_DIRECTORY[],但因?yàn)?marshal( fixed array [16]) 的修飾,此數(shù)組已經(jīng)從引用語(yǔ)義改為值語(yǔ)義。不過(guò)這樣做還是會(huì)受到一些限制,如不能多層嵌套、使用時(shí)性能受到影響等等。

        除了上述兩種在結(jié)構(gòu)定義本身做文章的解決方法,還可以從結(jié)構(gòu)的操作上做文章。

        此類結(jié)構(gòu)除了對(duì)結(jié)構(gòu)內(nèi)數(shù)組的訪問(wèn)外,主要的操作類型就是從內(nèi)存塊或輸入流中讀取整個(gè)結(jié)構(gòu),因此完全可以使用 CLR 提高的二進(jìn)制序列化支持,通過(guò)實(shí)現(xiàn)自定義序列化函數(shù)來(lái)完成數(shù)據(jù)的載入和保存,如:
        以下內(nèi)容為程序代碼:


        [Serializable]
        public struct IMAGE_OPTIONAL_HEADER : ISerializable
        {
        public const int IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;

        public ushort Magic;

        public uint NumberOfRvaAndSizes;

        public IMAGE_DATA_DIRECTORY[] DataDirectory;

        public IMAGE_OPTIONAL_HEADER(IntPtr ptr)
        {
        Magic = 0;
        NumberOfRvaAndSizes = 0;

        DataDirectory = new IMAGE_DATA_DIRECTORY[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
        }

        [SecurityPermissionAttribute(SecurityAction.Demand,SerializationFormatter=true)]
        public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
        {
        // 完成序列化操作
        }
        }



        這種解決方法可以將結(jié)構(gòu)的載入和存儲(chǔ),與結(jié)構(gòu)的內(nèi)部表現(xiàn)完全分離開(kāi)來(lái)。雖然結(jié)構(gòu)內(nèi)部保存的只是數(shù)組引用,但用戶并不需關(guān)心。但缺點(diǎn)是必須為每個(gè)結(jié)構(gòu)都編寫相應(yīng)的序列化支持代碼,編寫和維護(hù)都比較麻煩。

        與此思路類似的是我比較喜歡的一種解決方法,通過(guò)一個(gè)公共工具基類以 Reflection 的方式統(tǒng)一處理,如:
        以下內(nèi)容為程序代碼:

        public class IMAGE_OPTIONAL_HEADER : BinaryBlock
        {
        public const int IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;

        public ushort Magic;

        public uint NumberOfRvaAndSizes;

        public IMAGE_DATA_DIRECTORY[] DataDirectory = new IMAGE_DATA_DIRECTORY[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
        }



        注意原本的 struct 在這兒已經(jīng)改為 class,因?yàn)橥ㄟ^(guò)這種方式已經(jīng)沒(méi)有必要非得固守值類型的內(nèi)存模型。BinaryBlock 是一個(gè)公共的工具基類,負(fù)責(zé)通過(guò) Reflection 提供類型的載入和存儲(chǔ)功能,如
        以下內(nèi)容為程序代碼:

        public class BinaryBlock
        {
        private static readonly ILog _log = LogManager.GetLogger(typeof(BinaryBlock));

        public BinaryBlock()
        {
        }

        static public object LoadFromStream(BinaryReader reader, Type objType)
        {
        if(objType.Equals(typeof(char)))
        {
        return reader.ReadChar();
        }
        else if(objType.Equals(typeof(byte)))
        {
        return reader.ReadByte();
        }
        //...
        else if(objType.Equals(typeof(double)))
        {
        return reader.ReadDouble();
        }
        else if(objType.IsArray)
        {
        // 處理數(shù)組的情況
        }
        else
        {
        foreach(FieldInfo field in ClassType.GetFields())
        {
        field.SetValue(obj, LoadFromStream(...));
        }
        }

        return true;
        }

        public bool LoadFromStream(Stream stream)
        {
        return LoadFromStream(new BinaryReader(stream), this);
        }
        }



        LoadFromStream 是一個(gè)嵌套方法,負(fù)責(zé)根據(jù)指定字段類型從流中載入相應(yīng)的值。使用時(shí)只需要對(duì)整個(gè)類型調(diào)用此方法,則會(huì)自動(dòng)以 Reflection 機(jī)制,遍歷類的所有字段進(jìn)行處理,如果有嵌套定義的情況也可以直接處理。使用此方法,類型本身的定義基本上就無(wú)需擔(dān)心載入和存儲(chǔ)機(jī)制,只要從 BinaryBlock 類型繼承即可。有興趣的朋友還可以對(duì)此類進(jìn)一步擴(kuò)展,支持二進(jìn)制序列化機(jī)制。

        此外 C# 2.0 中為了解決此類問(wèn)題提供了一個(gè)新的 fixed array 機(jī)制,支持在結(jié)構(gòu)中直接定義內(nèi)嵌值語(yǔ)義的數(shù)組,如
        以下內(nèi)容為程序代碼:

        struct data
        {
        int header;
        fixed int values[10];
        }



        此結(jié)構(gòu)在編譯時(shí)由編譯器將數(shù)組字段翻譯成一個(gè)外部值類型結(jié)構(gòu),以實(shí)現(xiàn)合適的空間布局,如
        以下內(nèi)容為程序代碼:

        .class private sequential ansi sealed beforefieldinit data
        extends [mscorlib]System.ValueType
        {
        .class sequential ansi sealed nested public beforefieldinit '<values>e__FixedBuffer0'
        extends [mscorlib]System.ValueType
        {
        .pack 0
        .size 40
        .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 [img]/images/wink.gif[/img]
        .field public int32 FixedElementField
        } // end of class '<values>e__FixedBuffer0'

        .field public int32 header
        .field public valuetype data/'<values>e__FixedBuffer0' values
        .custom instance void [mscorlib]System.Runtime.CompilerServices.FixedBufferAttribute::.ctor(class [mscorlib]System.Type, int32) = ( ...)
        } // end of class data



        可以看到 values 字段被編譯成一個(gè)值類型,而值類型本身使用的是類似于上述第一種解決方法的思路,強(qiáng)行限制結(jié)構(gòu)長(zhǎng)度。而在使用時(shí),也完全是類似于第一種解決方法的 unsafe 操作,如對(duì)此數(shù)組的訪問(wèn)被編譯成 unsafe 的指針操作:
        以下內(nèi)容為程序代碼:

        // 編譯前
        for(int i=0; i<10; i++)
        d.values[i] = i;

        // 編譯后
        for(int i=0; i<10; i++)
        &data1.values.FixedElementField[(((IntPtr) i) * 4)] = i;



        不幸的是這種方式必須通過(guò) unsafe 方式編譯,因?yàn)槠鋬?nèi)部都是通過(guò) unsafe 方式實(shí)現(xiàn)的。而且也只能處理一級(jí)的嵌套定義,如果將 IMAGE_OPTIONAL_HEADER 的定義轉(zhuǎn)換過(guò)來(lái)會(huì)得到一個(gè) CS1663 錯(cuò)誤:
        以下內(nèi)容為程序代碼:

        error CS1663: Fixed sized buffer type must be one of the following: bool, byte, short, int, long, char, sbyte, ushort, uint, ulong, float or double




        Eric Gunnerson 有篇文章, Arrays inside of structures,簡(jiǎn)要介紹了 C# 2.0 中的這種有限度的

        溫馨提示:喜歡本站的話,請(qǐng)收藏一下本站!

        本類教程下載

        系統(tǒng)下載排行

        主站蜘蛛池模板: 日韩一级片免费观看| 亚洲AV无码乱码在线观看富二代| 九九久久精品国产免费看小说| 在线观看免费人成视频色| 在线播放高清国语自产拍免费| 日韩在线免费电影| 精品国产亚洲男女在线线电影 | 在线观看亚洲电影| 国产性生交xxxxx免费| 校园亚洲春色另类小说合集| 又爽又高潮的BB视频免费看| 黄色免费在线观看网址| 最近中文字幕mv免费高清在线 | 一级黄色免费网站| 最新国产AV无码专区亚洲 | 亚洲av成人综合网| 野花高清在线观看免费完整版中文| 亚洲专区中文字幕| 国产精品久久香蕉免费播放| 免费夜色污私人影院网站电影| 可以免费看黄的网站| 亚洲av无码专区在线观看亚| 69精品免费视频| 亚洲一线产品二线产品| 91精品全国免费观看含羞草| 亚洲1234区乱码| 一本色道久久88亚洲综合| 两个人看的www高清免费观看| 亚洲日韩中文字幕日韩在线| 亚洲狠狠婷婷综合久久| 国产免费av片在线看| 中文字幕亚洲综合久久| 最近中文字幕免费mv视频7| 在线播放免费人成视频网站| 色婷婷亚洲十月十月色天| 成人免费无码大片A毛片抽搐色欲| 一级视频在线免费观看| 亚洲AV无码一区二区三区在线| 国产极品粉嫩泬免费观看| 久久国产精品免费观看| 亚洲日韩一区精品射精|