中繼資料 API 的編碼慣例
本主題討論中繼資料 API 使用的編碼慣例。
處理字串參數
中繼資料 API 會以 Unicode 格式公開所有字串 (磁碟上符號名稱的格式實際上是 UTF-8,但這對中繼資料 API 用戶端是隱藏的)。每個傳回的字串都是三個參數的組合 (實際的參數名稱有所不同):
[in] ULONG cchString:將在其中傳回字串 (包括結束的 null 字元) 的緩衝區大小 (以位元組為單位)。
[out] LPCWSTR wzString:在其中傳回字串的緩衝區指標。
[out] ULONG *pchString:傳回之字串 (包括結束的 null 字元) 的大小指標。 如果緩衝區太小,而無法儲存完整字串,則會截斷傳回的字串、傳回錯誤表示,而且必要時用戶端可以重新配置緩衝區並重試。
符號名稱
下列慣例適用於字串參數的符號名稱:
做為符號名稱的字串參數一定會被假設為 null 結尾,而且不需要 [in] 長度參數。 不支援內嵌的 null 字元。
如果 [in] 參數字串太大,而必須截斷才能保存,則會傳回錯誤。
使用者字串
下列慣例適用於使用者提供的字串參數:
使用者字串可以有內嵌的 null 字元,但不應有 null 結束字元。
長度必須是在 cchString 參數中提供。 緩衝區大小必須是即將儲存之字串的確切長度。
儲存預設值
常數可以儲存至中繼資料做為欄位、參數和屬性的預設值。 以下三個參數會用來指定常數 (實際的參數名稱有所不同):
[in] DWORD dwCPlusTypeFlag:指定預設值型別之 CorElementType 列舉型別的值。
[in] void const *pValue:實際預設值的指標。 例如,保留 0x0000002A 的 4 位元組 DWORD 指標會將 42 (十進位) 的 DWORD 值儲存在中繼資料中。 預設值的型別 (在 dwCPlusTypeFlag 中指定) 限制為基本 (Primitive) 或字串。 如果 dwCPlusTypeFlag 為 ELEMENT_TYPE_CLASS,則預設值為 null。
[in] ULONG cchValue:位元組序列中 pValue 指向的 Unicode 字元數目。 只有在 dwCPlusTypeFlag 中指定的型別為 ELEMENT_TYPE_STRING 時,這才是必要的。 在所有其他情況下,則會從型別推斷長度。
預設值不會自動插入至初始化程式碼或靜態初始化的資料區域。 只是將其記錄在中繼資料中。
若要表示您不要指定預設值,請設定 dwCPlusTypeFlag 的所有位元 (也就是說,將此值設為 -1)。
傳回參數的 Null 指標
因為中繼資料 API 會執行少許錯誤檢查,在下列狀況下,預期是傳回參數的非 null 指標:
在 Define 方法中,傳回的語彙基元需要非 null 指標。 這些方法會建立您要定義的項目並且會傳回此項目的語彙基元。 如果不需要的話,您可以選擇捨棄語彙基元。
如果成功找到項目的語彙基元,Find 方法一定會傳回它。
在 Get 方法中,您可以在不需要的參數中傳遞 null。
Set 方法一般沒有傳回值。 您可以傳入要更新之項目的語彙基元和更新值,而且這些方法都會執行更新。
會被忽略的參數值
中繼資料 API 有數個方法可讓您變更先前已定義之項目的屬性。 下列範例會使用 IMetaDataEmit::SetFieldProps 方法來變更欄位的屬性,這些屬性先前是在 IMetaDataEmit::DefineField 呼叫中提供的:
HRESULT SetFieldProps(mdFieldDef fd, DWORD dwFieldFlags,
DWORD dwDefType, void const *pValue, ULONG cchValue)
有時,您可能想要變更 dwFieldFlags 但不變更 pValue (反之亦然)。 在此情況下,儘管您不要變更參數值,您也必須傳遞此值以避免錯誤。 不過,如果您不要變更值,則可以傳遞會指定應忽略此引數的特定值。 中繼資料 API 使用下列慣例表示方法引數應被忽略:
如果參數為指標型別,則傳遞 null 指標。
如果參數為實值型別 (一般為旗標位元遮罩),則傳遞已設定之所有位元的值 (–1)。
錯誤傳回
在 IMetaDataDispenserEx、IMetaDataEmit 和 IMetaDataImport 介面中,幾乎所有的方法都會傳回 HRESULT 值,來表示其結果。 如果作業成功,其值為 S_OK。 如果呼叫不成功,則會傳回另一個值來描述作業失敗的原因。
所有中繼資料 API 的一般模式是,如果呼叫端提供的字串緩衝區太小而無法保留結果,則 API 會複製適當數目的字元數,但會傳回 CLDB_S_TRUNCATION 的 HRESULT 值,而非 S_OK。
IMetadata 介面的呼叫端為編譯器或工具。 重要的是,這些呼叫端一定要檢查每個呼叫的傳回狀態,以便偵測錯誤。 在這些情況下,錯誤狀況是反映直接呼叫端 (例如編譯器) 一方的問題,而非使用者 (例如應用程式) 的問題。
記憶體管理
泛型 COM 預設為呼叫端釋放被呼叫端所配置的記憶體。 不過,中繼資料方法的運作方式不同。
許多中繼資料方法會傳回記憶體區塊的 [out] 指標。 該記憶體是模組中繼資料堆積的一部分,由 Common Language Runtime (CLR) 所擁有。 因此,會提供您 CLR 記憶體中儲存中繼資料的直接指標,而且您的應用程式不需要釋放該記憶體。
泛型支援
在 .NET Framework 2.0 版中,中繼資料 API 已大幅擴充,現在可支援泛型 (有時又稱為「參數的多型」)。 泛型有些類似 C++ 樣板。 在 C# 中定義泛型類別的範例可能如下:
public class Dictionary<Key, Val> { . . . }
在這個範例中,Dictionary 類別以兩個名為 Key 和 Val 的泛型參數來參數化。 當執行個體化此類別時,使用者可以選取泛型參數的型別,如下列範例所示:
Dictionary<string, int> NameToPhone = new Dictionary<string, int>();
Dictionary<int, string> PhoneToName = new Dictionary<int, string>();