2009年8月13日 星期四

在C#的結構(struct)中使用陣列(array)

C#是物件導向的語言,所以並不建議在其中使用結構
需要使用的時候最好儘量使用類別(class)來取代

不過有時候還是會需要定義結構,比如使用前人定義出的結構時
為了要和非.net(unmanagement)的程式庫(dll)溝通
還是需要定義出符合原本定義的結構,如果都只用到一些共通的形態的話
那在C#和C++中的定義基本上是一樣的

struct t1
{
char c;
int i;
};

不過這時候如果結構裡面有定義陣列的話

struct t2
{
char c;
int i[10];
};

因為C#裡面並沒有單純陣列的概念
所以基本上都要宣告成陣列物件

struct t2cs
{
char c;
int[] i;
};

這時候會發現沒辦法在這結構裡面直接宣告陣列的大小
因為物件不能在宣告的時候給值,必須要在new出物件的時候才能給定
記憶體的大小會跟非net的結構不同,所以傳遞過去也沒辦法取用

這時候可以使用unsafe和fixed來處理,
將這個程式區段設定為unmanagement的形態

unsafe struct t2cs
{
char c;
fixed int i[10];
};

雖然這種宣告方法很直覺
不過這樣的宣告需要去專案屬性裡面
將編譯選項裡面的容許unsafe選項打開
而且這個方法僅適用於一般形態的陣列(int, char...)
如果是結構的陣列就會出現問題

struct t3
{
char c;
t1 t[10];
};

那就沒辦法使用fixed來定義了
這時候可以用MarshalAs來預留一塊記憶體位置

struct t3cs
{
char c;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=10)]
t1[] t;
};

這時候如果發現兩邊的記憶體大小值不一樣的話
可以參考結構(struct)的大小問題來限定記憶體的配置方式
之後就可以直接接收物件來使用或是傳遞迴去了

--
參考資料
Fixed Size Buffers (C# Programming Guide)
[C#] Array in struct
Marshal Unmanaged struct to managed code using c#
UnmanagedType 列舉型別

1 則留言:

  1. 請教大師,如果遇到二維陣列的話,C#用 MarshalAs 該如果處置?ex:t1 t[10][5]

    回覆刪除