1、Array in stack
對於這樣的struct:typedef struct { int XY[2]; } Point2D;
要在.NET為一個非託管函數傳遞這樣一個結構體,原來得這樣定義:
struct Point2D
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst=2)]
public int[] XY;
}
現在可以這麼寫(不過得用unsafe上下文):
unsafe struct Point2D
{
public fixed int XY[2];
}
不過這個功能還非常有限,不知道是出於什麼原因考慮,只允許在strcut裡面定義這樣的數組,並且只能使用bool, byte,
short, int, long, char, sbyte, ushort, uint, ulong, float和double這樣的
primitive類型。
也可以把數組作為局部變數分配在堆棧上,只是文法不太一樣,那就是stackalloc關鍵字:int* fib = stackalloc int[100];,也要unsafe上下文。這可以提高不少效率。這是.NET 1.x就有的功能,只是似乎沒人用這個
PS. 對於只允許使用primitive類型,我認為是沒道理的,最起碼應該允許所有實值型別的棧內數組。設計者們為啥這麼考慮呢?怕堆疊溢位?據我測試.NET的堆棧空間也是1M左右,大部分情況下這麼大的棧空間都被浪費了。
2、Function pointer as a return value
在.NET 1.x做P/Invoke時,對於那些回呼函數,可以使用Delegate類型的參數作為函數指標傳入。但有些非託管函數的傳回值也是個函數指標,此時.NET 1.1變得無能為力,要調用這個函數,你得用native代碼再寫個封裝,總之很麻煩。
.NET 2.0的System.Runtime.InteropServices.Marshal類為此需求新增了兩個方法:
public static Delegate GetDelegateForFunctionPointer (
IntPtr ptr,
Type t
);
public static IntPtr GetFunctionPointerForDelegate (
Delegate d
);
3、Marshal過程支援更多的類型
這是一個很細的問題,比如這樣一個非託管struct:
typedef struct { Point2D XYZ[3]; } Point2DX3;
對應到C#,你也許會這樣寫:
struct Point2D
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst=2)]
public int[] XY;
}
struct Point2DX3
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst=3)]
public Point2D[] XYZ;
}
事實上這是不可行的。在.NET 1.x,結構體內嵌定長數組的類型必須是primitive類型,否則不能進行marshal過程。事實上調用Marshal.SizeOf時,會彈出異常說“無法得到大小”云云
.NET 2.0把這個問題給改了(與其說是個增強,還不如說是修正了這個bug),Marshal.SizeOf(typeof(Point2DX3))現在可以正常運行,輸出24 == 3 * 2 * sizeof(int)。
讓Marshal.SizeOf正常工作非常重要,得不到對象的大小,記憶體對齊都無法保證,marshal過程根本就不可行。
此外,正常工作的Marshal.SizeOf可以使這種“序列化”方式也總能正常工作(這在我以前的blog貼過,這裡是Generic版本):
unsafe class BinarySerializer
{
public static byte[] Struct2Bytes<T>(T obj)
{
int size = Marshal.SizeOf(obj);
byte[] bytes = new byte[size];
fixed (byte* pb = &bytes[0])
{
Marshal.StructureToPtr(obj, (IntPtr)pb, true);
}
return bytes;
}
public static T Bytes2Struct<T>(byte[] bytes)
{
fixed (byte* pb = &bytes[0])
{
return (T)Marshal.PtrToStructure((IntPtr)pb, typeof(T));
}
}
}
和.NET內建的序列化機制相比,這個方案效率要高得多。和.NET內建的序列化機制相似,這裡需要對象或結構體內部的所有成員都能使用
Marshal.SizeOf得到其大小,對應於“對象或結構體內部的所有成員都帶有[Serializable]特性或者實現了
ISerializable介面”。
PS. 這個序列化方案效率雖高,但不如.NET內建的方案可靠。.NET的編譯器可以檢查一個
類型能否支援序列化,而這個方案得由程式員判斷Marshal.SizeOf能否工作(比如Marshal.SizeOf絕對不會知道string類型實
際佔用多少記憶體,也就是說類型執行個體的大小必須是固定的才行)。