国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發(fā) > 綜合 > 正文

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

2024-07-21 02:19:11
字體:
供稿:網(wǎng)友
  • 本文來源于網(wǎng)頁設(shè)計(jì)愛好者web開發(fā)社區(qū)http://www.html.org.cn收集整理,歡迎訪問。
  • 在 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í)直接訪問結(jié)構(gòu)所在內(nèi)存塊。但在 c# 這類語言中,則無法直接如此使用,因?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: 語法錯(cuò)誤,錯(cuò)誤的數(shù)組聲明符。若要聲明托管數(shù)組,秩說明符應(yīng)位于變量標(biāo)識(shí)符之前




    如果改用 c# 中引用類型的類似定義語法,如
    以下內(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 的初始化工作不同。如此一來只能將數(shù)組的初始化放到構(gòu)造函數(shù)中,而且結(jié)構(gòu)還不能有無參數(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];
    }
    }



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

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

    另外一種解決方法是通過 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ì)來說要優(yōu)雅一些,通過 marshal 機(jī)制支持的屬性來定義值數(shù)組語義,使用起來與普通的數(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)從引用語義改為值語義。不過這樣做還是會(huì)受到一些限制,如不能多層嵌套、使用時(shí)性能受到影響等等。

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

    此類結(jié)構(gòu)除了對(duì)結(jié)構(gòu)內(nèi)數(shù)組的訪問外,主要的操作類型就是從內(nèi)存塊或輸入流中讀取整個(gè)結(jié)構(gòu),因此完全可以使用 clr 提高的二進(jìn)制序列化支持,通過實(shí)現(xiàn)自定義序列化函數(shù)來完成數(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)完全分離開來。雖然結(jié)構(gòu)內(nèi)部保存的只是數(shù)組引用,但用戶并不需關(guān)心。但缺點(diǎn)是必須為每個(gè)結(jié)構(gòu)都編寫相應(yīng)的序列化支持代碼,編寫和維護(hù)都比較麻煩。

    與此思路類似的是我比較喜歡的一種解決方法,通過一個(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)橥ㄟ^這種方式已經(jīng)沒有必要非得固守值類型的內(nèi)存模型。binaryblock 是一個(gè)公共的工具基類,負(fù)責(zé)通過 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)行處理,如果有嵌套定義的情況也可以直接處理。使用此方法,類型本身的定義基本上就無需擔(dān)心載入和存儲(chǔ)機(jī)制,只要從 binaryblock 類型繼承即可。有興趣的朋友還可以對(duì)此類進(jìn)一步擴(kuò)展,支持二進(jìn)制序列化機(jī)制。

    此外 c# 2.0 中為了解決此類問題提供了一個(gè)新的 fixed array 機(jī)制,支持在結(jié)構(gòu)中直接定義內(nèi)嵌值語義的數(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)長度。而在使用時(shí),也完全是類似于第一種解決方法的 unsafe 操作,如對(duì)此數(shù)組的訪問被編譯成 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;



    不幸的是這種方式必須通過 unsafe 方式編譯,因?yàn)槠鋬?nèi)部都是通過 unsafe 方式實(shí)現(xiàn)的。而且也只能處理一級(jí)的嵌套定義,如果將 image_optional_header 的定義轉(zhuǎn)換過來會(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,簡要介紹了 c# 2.0 中的這種有限度的增強(qiáng)語法。
    發(fā)表評(píng)論 共有條評(píng)論
    用戶名: 密碼:
    驗(yàn)證碼: 匿名發(fā)表
    主站蜘蛛池模板: 通辽市| 甘泉县| 德清县| 民勤县| 宽城| 乌拉特中旗| 社会| 广昌县| 大英县| 高州市| 福贡县| 蓬溪县| 田阳县| 韩城市| 桦川县| 临猗县| 玉龙| 镇坪县| 常州市| 镶黄旗| 绵竹市| 池州市| 江川县| 卢氏县| 德庆县| 都江堰市| 东莞市| 白河县| 贵阳市| 西平县| 佛山市| 阿巴嘎旗| 辽阳市| 奇台县| 于田县| 湖州市| 青州市| 蓝田县| 乌海市| 藁城市| 临猗县|