Microsoft介面定義語言 3.0 簡介
Microsoft介面定義語言 (MIDL) 3.0 是簡化的新式語法,用於在介面定義語言 (IDL) 檔案 (.idl
檔案) 內定義 Windows 執行時間類型。 對於任何熟悉 C、C++、C# 和/或 Java 的用戶,這個新語法會感到熟悉。 MIDL 3.0 是定義 C++/WinRT 運行時間類別的特別方便方式,比舊版 IDL 大幅精簡(將設計減少三分之二的長度,並使用合理的預設值來減少裝飾屬性的需求)。
以下是 MIDL 3.0 的外觀;此範例示範您可能會使用的大部分語言語法元素。
// Photo.idl
namespace PhotoEditor
{
delegate void RecognitionHandler(Boolean arg); // delegate type, for an event.
runtimeclass Photo : Windows.UI.Xaml.Data.INotifyPropertyChanged // interface.
{
Photo(); // constructors.
Photo(Windows.Storage.StorageFile imageFile);
String ImageName{ get; }; // read-only property.
Single SepiaIntensity; // read-write property.
Windows.Foundation.IAsyncAction StartRecognitionAsync(); // (asynchronous) method.
event RecognitionHandler ImageRecognized; // event.
}
}
請注意,MIDL 3.0 的語法是專為定義 類型 midl.exe
8.01.0622 版或更新版本,搭配 /winrt
參數使用)。
注意
另請參閱 Windows 執行時間合併參考 (Windows 執行時間類型系統和 Windows 元數據檔案]。
MIDL 1.0、2.0 和 3.0
介面定義語言 (IDL) 始於分散式運算環境/遠端過程調用 (DCE/RPC) 系統。 原始 MIDL 1.0 是 DCE/RPC IDL,具有定義 COM 介面和共同類別的增強功能。
更新的 MIDL 2.0 語法(也稱為 MIDLRT)接著會在 Microsoft 內開發,以宣告 Windows 平臺的 Windows 運行時間 API。 如果您查看 Windows SDK 資料夾 %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\winrt
,您會看到使用 MIDL 2.0 語法撰寫的 .idl
檔案範例。 這些是內建的 Windows 運行時間 API,其應用程式二進位介面 (ABI) 形式宣告。 這些檔案主要是為了工具使用,您不會以這個形式撰寫或取用這些 API(除非您撰寫非常低階的程式碼)。
另請參閱從傳統 MIDLRT轉換至 MIDL 3.0
MIDL 3.0 是更簡單且更現代的語法,其用途是宣告 Windows 運行時間 API。 而且您可以在專案中使用它,特別是定義 C++/WinRT 運行時間類別。 適用於內建 Windows 執行時間 API C++/WinRT 的標頭是 SDK 的一部分,位於資料夾內 %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt
。
MIDL 3.0 的使用案例
一般而言,所有 Windows 運行時間 API 都是設計成可供所有 Windows 執行時間語言投影使用。 這部分是藉由選擇獨佔方式將 Windows 執行時間類型傳遞至 Windows 執行時間 API 和從 Windows 運行時間 API 來完成。 雖然它是將原始 COM 介面傳遞至 Windows 執行時間 API 以及從 Windows 執行時間 API 傳遞的有效設計決策,但這樣做會限制該特定 Windows 運行時間 API 的取用者C++應用程式。 在互操作案例中可以看到這項技術,例如,在 Direct3D 與 XAML 之間互操作時。 由於 Direct3D 位於圖片中,因此案例必須縮小為 C++ 應用程式。 因此,需要 COM 介面的 API 不會對固有的內容施加任何額外的限制。 例如,C++應用程式可以取得 IDXGISwapChain 介面指標,然後將該指標傳遞給 ISwapChainPanelNative::SetSwapChain 方法。 例如,C# 應用程式無法取得開頭的 IDXGISwapChain,因此無法基於該原因使用該方法。 這些 Interop 相關例外狀況會存在於 interop 標頭中,例如 windows.ui.xaml.media.dxinterop.h
。
如果有 COM 元件的特性或功能,您想要公開至C++以外的 Windows 運行時間語言投影,則您可以 C++ 建立直接建立及使用 COM 元件(例如 DirectX)的 windows 運行時間元件 (WRC),並以 Windows 運行時間 API 介面的形式公開其部分特性和功能的複寫,並公開其部分特性和功能的複寫,而 Windows 運行時間 API 介面會採用並傳回 Windows僅限運行時間類型。 然後,您可以從以任何 Windows 執行時間語言投影所撰寫的應用程式取用該 WRC。
定義結構,並從命令行呼叫 midl.exe
MIDL 3.0 定義中的重要組織概念是命名空間、類型和成員。 MIDL 3.0 原始程式檔(.idl
檔案)包含至少一個命名空間,其中是類型和/或次級命名空間。 每個類型都包含零個或多個成員。
- 類別、介面、結構和列舉是類型。
- 方法、屬性、事件和欄位是成員的範例。
當您編譯 MIDL 3.0 原始程式檔時,編譯程式(midl.exe
)會發出 Windows 運行時間元數據檔案(通常是 .winmd
檔案)。
// Bookstore.idl
namespace Bookstore
{
runtimeclass BookSku : Windows.UI.Xaml.Data.INotifyPropertyChanged
{
BookSku();
BookSku(Single price, String authorName, String coverImagePath, String title);
Single Price;
String AuthorName{ get; };
Windows.UI.Xaml.Media.ImageSource CoverImage{ get; };
String CoverImagePath{ get; };
String Title{ get; };
Boolean Equals(BookSku other);
void ApplyDiscount(Single percentOff);
}
}
由於 Windows 執行時間類型的命名空間會成為類型名稱的一部分,因此上述範例會定義名為 Bookstore.BookSku 的運行時間類別。 沒有與語言無關的方式可以表達 BookSku,也不需要表達命名空間。
這個類別會實作 Windows.UI.Xaml.Data.INotifyPropertyChanged 介面。 類別包含數個成員:兩個建構函式、一個讀寫屬性(Price)、一些只讀屬性(AuthorName 透過 Title),以及兩個 方法,名為 equals 和 ApplyDiscount。 請注意,Single 類型的使用,而不是 float。 而且,String 有大寫 “S”。
提示
Visual Studio 透過 C++/WinRT Visual Studio 延伸模組 (VSIX) 提供編譯 MIDL 3.0 的最佳體驗。 請參閱 Visual Studio C++/WinRT 支援,以及 VSIX。
但您也可以從命令行編譯 MIDL 3.0。 如果此範例的原始碼儲存在名為 Bookstore.idl
的檔案中,您可以發出下列命令。 如有需要,您可以更新 命令中使用的 SDK 版本號碼(也就是 10.0.17134.0)。
midl /winrt /metadata_dir "%WindowsSdkDir%References\10.0.17134.0\windows.foundation.foundationcontract\3.0.0.0" /h "nul" /nomidl /reference "%WindowsSdkDir%References\10.0.17134.0\Windows.Foundation.FoundationContract\3.0.0.0\Windows.Foundation.FoundationContract.winmd" /reference "%WindowsSdkDir%References\10.0.17134.0\Windows.Foundation.UniversalApiContract\6.0.0.0\Windows.Foundation.UniversalApiContract.winmd" /reference "%WindowsSdkDir%\References\10.0.17134.0\Windows.Networking.Connectivity.WwanContract\2.0.0.0\Windows.Networking.Connectivity.WwanContract.winmd" Bookstore.idl
midl.exe
工具會編譯範例,併產生名為 Bookstore.winmd
的元數據檔案(根據預設,會使用 .idl
檔的名稱)。
提示
如果您使用多個 IDL 檔案(如需相關建議,請參閱 將運行時間類別分解成 Midl 檔案 (.idl)),然後將所有產生的 .winmd
檔案合併成與根命名空間同名的單一檔案。 最後一個 .winmd
檔案將是 API 取用者將參考的檔案。
在此情況下,BookSku 是 Bookstore 命名空間中唯一的運行時間類別,因此我們已儲存步驟,並只命名命名空間的 .idl
檔案。
順便說一句,您可以使用 where
命令來找出安裝 midl.exe
的位置。
where midl
如果您想要從不同的 .idl
檔案使用一個 .idl
檔案中定義的類型,請使用 import
指示詞。 如需詳細資訊和程式代碼範例,請參閱 XAML 控件;繫結至 C++/WinRT 屬性。 當然,如果您要取用內建或第三方元件,則您無法存取 .idl
檔案。 例如,您可能想要使用 Win2D Windows 運行時間 API 進行即時模式 2D 圖形轉譯。 上述命令使用 /reference
參數來參考 Windows 執行時間元資料 (.winmd
) 檔案。 在下一個範例中,我們將再次使用該參數,以想像我們有 Bookstore.winmd
但未 Bookstore.idl
的案例。
// MVVMApp.idl
namespace MVVMApp
{
runtimeclass ViewModel
{
ViewModel();
Bookstore.BookSku BookSku{ get; };
}
}
如果上述範例的原始碼儲存在名為 MVVMApp.idl
的檔案中,您可以發出下列命令來參考 Bookstore.winmd
。
midl /winrt /metadata_dir "%WindowsSdkDir%References\10.0.17134.0\windows.foundation.foundationcontract\3.0.0.0" /h "nul" /nomidl /reference "%WindowsSdkDir%References\10.0.17134.0\Windows.Foundation.FoundationContract\3.0.0.0\Windows.Foundation.FoundationContract.winmd" /reference "%WindowsSdkDir%References\10.0.17134.0\Windows.Foundation.UniversalApiContract\6.0.0.0\Windows.Foundation.UniversalApiContract.winmd" /reference "%WindowsSdkDir%\References\10.0.17134.0\Windows.Networking.Connectivity.WwanContract\2.0.0.0\Windows.Networking.Connectivity.WwanContract.winmd" /reference Bookstore.winmd MVVMApp.idl
命名空間
需要命名空間。 它會在命名空間區塊範圍中定義之所有類型的名稱前面加上命名空間名稱。 命名空間也可以包含次級命名空間宣告。 在次級命名空間範圍中定義的型別名稱具有所有包含命名空間名稱的前置詞。
下列範例是宣告相同 Windows.Foundation.Uri 類別的兩種方式(如您所見,句號會分隔巢狀命名空間的層級)。
namespace Windows.Foundation
{
runtimeclass Uri : IStringable
{
...
}
}
namespace Windows
{
namespace Foundation
{
runtimeclass Uri : IStringable
{
...
}
}
}
以下是另一個範例,示範以巢狀方式宣告命名空間及其類型是合法的。
namespace RootNs.SubNs1
{
runtimeclass MySubNs1Class
{
void DoWork();
}
namespace SubNs2
{
runtimeclass MySubNs2Class
{
void DoWork();
}
}
}
但更常見的做法是關閉先前的命名空間,並開啟新的命名空間,如下所示。
namespace RootNs.SubNs1
{
runtimeclass MySubNs1Class
{
void DoWork();
}
}
namespace RootNs.SubNs1.SubNs2
{
runtimeclass MySubNs2Class
{
void DoWork();
}
}
類型
MIDL 3.0 中有兩種數據類型:實值型別和參考型別。 實值型別的變數會直接包含其數據。 參考類型的變數會儲存其數據的參考(這類變數也稱為 物件)。
有兩個參考型別變數可以參考相同的物件。 因此,一個變數上的作業會影響另一個變數所參考的物件。 使用實值類型時,變數各有自己的數據複本,而且一個上的作業無法影響另一個。
MIDL 3.0 的實值型別會進一步分為簡單型別、列舉型別、結構型別和可為 Null 型別。
MIDL 3.0 的參考型別會進一步分成類別類型、介面類型和委派類型。
以下是 MIDL 3.0 類型系統的概觀。 與舊版 MIDL 不同,您無法針對這些類型使用別名。
類別 | 描述 | |
---|---|---|
實值類型 | 簡單類型 | 帶正負號整數:Int16、Int32、Int64 |
不帶正負號整數:UInt8、UInt16、UInt32、UInt64 | ||
Unicode 字元:Char (代表 UTF-16LE;16 位 Unicode 字碼單位) | ||
Unicode 字串:String | ||
IEEE 浮點數:單一、Double | ||
布爾值:布爾值 | ||
128 位 UUID:Guid | ||
列舉類型 | 表單的使用者定義類型 列舉 E {...} | |
結構類型 | 表單的使用者定義類型 結構 S {...} | |
可為 Null 的類型 | 具有 null 值之所有其他實值型別的延伸 | |
參考型別 | 類別類型 | 所有其他類型的終極基類:Object |
執行時間類別 C {...} 表單的使用者定義類型 | ||
介面類型 | 表單的使用者定義類型 介面 I {...} | |
委派類型 | 表單的使用者定義型別 委派 <returnType> D(...) |
七個整數類型支援 8 位無符號數據;和 16 位、32 位和 64 位值,以帶正負號或不帶正負號的形式。
兩個浮點類型,Single 和 Double,分別代表使用 32 位單精度和 64 位雙精確度 IEEE 754 格式的數據。
MIDL 3.0 的 布爾值 類型代表布爾值;true
或 false
。
MIDL 3.0 中的字元和字串包含 Unicode 字元。 Char 類型代表 UTF-16LE 程式代碼單位;和 String 類型代表 UTF-16LE 程式代碼單位的序列。
下表摘要說明 MIDL 3.0 的數值類型。
類別 | 位 | 類型 | 範圍/有效位數 |
---|---|---|---|
帶正負號整數 | 16 | Int16 | –32,768...32,767 |
32 | Int32 | –2,147,483,648...2,147,483,647 | |
64 | Int64 | –9,223,372,036,854,775,808...9,223,372,036,854,775,807 | |
不帶正負號整數 | 8 | UInt8 | 0...255 |
16 | UInt16 | 0...65,535 | |
32 | UInt32 | 0...4,294,967,295 | |
64 | UInt64 | 0...18,446,744,073,709,551,615 | |
浮點 | 32 | 單一 | 1.5 × 10•45 至 3.4 × 1038,7 位數精確度 |
64 | 雙 | 5.0 × 10324 至 1.7 × 10308,15 位數有效位數 |
MIDL 3.0 來源檔案會使用類型定義來建立新的類型。 類型定義會指定新型別的名稱和成員。 這些 MIDL 3.0 類型類別是用戶可定義的。
- 屬性類型,
- 結構類型、
- 介面類型,
- runtimeclass 類型,
- 委派類型, 和
- 列舉類型。
屬性 類型定義可套用至其他類型定義的 Windows 執行時間屬性。 屬性會提供套用屬性之類型的相關元數據。
結構 類型會定義包含數據成員(fields) 的 Windows 執行時間結構。 結構是實值型別,而且不需要堆積配置。 結構類型的數據成員必須是實值型別或可為 Null 的類型。 結構類型不支持繼承。
介面 類型定義 Windows 執行時間介面,這是一組具名的函式成員。 介面可以指定介面的實作也必須實作一或多個指定的其他 (必要) 介面。 每個介面類型都直接衍生自 Windows 運行時間 IInspectable 介面。
runtimeclass 類型會定義 Windows 執行時間類別(運行時間類別)。 運行時間類別包含可以是屬性、方法和事件的成員。
委派 類型會定義 Windows 執行時間委派,代表具有特定參數清單和傳回型別之方法的參考。 委派可讓您將方法視為可傳遞為參數的實體。 委派類似於某些其他語言中找到的函式指標概念。 不同於函式指標,委派是面向物件,且類型安全。
列舉 類型是具有具名常數的不同類型。 每個列舉類型都有隱含的基礎類型;Int32 或 UInt32。 列舉類型的值集合與基礎類型的值集相同。
MIDL 3.0 支援三個額外的類型類別。
- 單一維度數位類型,
- 可為 Null 的實值型別和
- 物件 類型。
您不需要宣告單一維度陣列,才能使用它。 相反地,陣列類型是依照具有方括弧的類型名稱來建構。 例如,Int32[] 是 Int32的單維陣列。
同樣地,可為 Null 的 實值型別也不需要定義,才能使用它們。 針對每個不可為 Null 的實值類型 null
。 另請參閱 IReference<T>。
最後,MIDL 3.0 支援 Object 類型,其對應至 Windows 運行時間 IInspectable 介面。 介面 和 runtimeclass 參考型別在概念上衍生自 Object 類型;委派 則不會。
列舉值中的表達式
使用 MIDL 3.0 時,您只能在列舉型別具名常數的值定義中使用 表達式;換句話說,在列舉初始化表達式中。
表達式是從 操作數 和 運算子建構。 表達式中的運算子會指出要套用至操作數的作業。 運算子範例包括 +、-、*、/和 new
。 操作數的範例包括常值、字段、局部變數和表達式。
當表達式包含多個運算符時,運算子 優先順序 會控制個別運算符的評估順序。 例如,表達式 x + y * z 會評估為 x + (y * z),因為 * 運算子的優先順序高於 + 運算符。 邏輯作業的優先順序低於位運算。
下表摘要說明 MIDL 3.0 的運算符,依優先順序從最高到最低的順序列出運算符類別。 相同類別中的運算符優先順序相等。
類別 | 表達 | 描述 |
---|---|---|
主要 | x++ | 遞增後 |
x-- | 遞減後 | |
元 | +x | 身份 |
-x | 否定 | |
!x | 邏輯否定 | |
~x | 位否定 | |
++x | 預先遞增 | |
--x | 遞減前 | |
乘法 | x * y | 乘法 |
x / y | 劃分 | |
x % y | 剩餘 | |
添加劑 | x + y | 加法、字串串連、委派組合 |
x – y | 減法、委派移除 | |
轉變 | x << y | 左移 |
x >> y | 右移 | |
位 AND | x & y | 整數位 AND |
位 XOR | x ^ y | 整數位 XOR |
位 OR | x |y | 整數位 OR |
邏輯 AND | x && y | 布爾邏輯 AND |
邏輯 OR | x ||y | 布爾邏輯 OR |
類
類別(或運行時間類別)是 MIDL 3.0 類型中最基本的類別。 類別是單一單位中方法、屬性和事件的匯總定義。 類別支援 繼承 和 多型—衍生類別 可以擴充和特 製化基類的機制。
您可以使用類別定義來定義新的類別類型。 類別定義會以標頭開頭,指定類別 runtimeclass
關鍵詞、類別的名稱、基類(如果指定),以及類別所實作的介面。 標頭後面接著類別主體,其中包含分隔符 { 和 }之間寫入的成員宣告清單。
以下是名為 Area之簡單類別的定義。
runtimeclass Area
{
Area(Int32 width, Int32 height);
Int32 Height;
Int32 Width;
static Int32 NumberOfAreas { get; };
}
這會定義名為
根據預設,運行時間類別是密封的,而且不允許衍生自它。 請參閱
若要將 XAML 系結至檢視模型,必須在 MIDL 中定義檢視模型運行時間類別。 請參閱 XAML 控制件;如需詳細資訊,請系結至C++/WinRT 屬性。
您可以將運行時間類別定義前面加上 static
關鍵詞,宣告類別不支持實例(因此必須只包含靜態成員)。 將非靜態成員新增至 類別,然後會導致編譯錯誤。
static runtimeclass Area
{
static Int32 NumberOfAreas { get; };
}
靜態類別與空類別不同。 請參閱 空類別。
您可以使用 partial
關鍵詞,在運行時間類別定義前面加上 ,以指出類別定義不完整。 編譯程式遇到的所有部分類別定義都會合併成單一運行時間類別。 這項功能主要適用於 XAML 撰寫案例,其中部分類別是機器產生的。
修飾語 | 意義 |
---|---|
靜態的 | 類別沒有實例。 因此,只允許靜態成員。 |
部分 | 類別定義不完整。 |
如需進階修飾詞,請參閱 組合和啟用。
成員存取修飾詞
由於 MIDL 3.0 是描述 Windows 執行時間類型公用介面的定義語言,因此不需要明確語法來宣告成員的公用輔助功能。 所有成員都是隱含公開的。 這就是為什麼 MIDL 3.0 不需要也不允許 (有效備援) public
關鍵詞。
基類
類別定義可以遵循類別名稱和類型參數的冒號和基類名稱來指定基類。 省略基類規格與衍生自類型 Object 相同(換句話說,從 IInspectable)。
注意
您的檢視模型類別事實上,您在應用程式中定義的任何運行時間類別都不需要衍生自基類。
您在應用程式中定義的任何運行時間類別, 衍生自基類,稱為 可組合 類別。 而且有可組合類別的條件約束。 若要讓應用程式通過 Windows 應用程式認證套件 Visual Studio 和 Microsoft 市集用來驗證提交的測試(因此,讓應用程式成功擷取到 Microsoft 市集),組合類別最終必須衍生自 Windows 基類。 這表示繼承階層根目錄的 類別必須是源自 Windows.* 命名空間的類型。
在下一個範例中,Volume 的基類是 Area,而 Area 的基類 Windows.UI.Xaml.DependencyObject。
unsealed runtimeclass Area : Windows.UI.Xaml.DependencyObject
{
Area(Int32 width, Int32 height);
Int32 Height;
Int32 Width;
}
runtimeclass Volume : Area
{
Volume(Int32 width, Int32 height, Int32 depth);
Int32 Depth;
}
注意
在這裡,區域 和 磁碟區 定義在相同的原始程序檔中。 如需優缺點的討論,請參閱將 Factoring 執行時間類別 Midl 檔案 (.idl)。
類別會繼承其基類的成員。 繼承表示類別隱含地包含其基類的所有成員,但基類的建構函式除外。 衍生類別可以將新成員新增至其繼承的成員,但無法移除繼承成員的定義。
在上一個範例中,Volume 會從 Area繼承 Height 和 Width 屬性。 因此,每個 Volume 實體都包含三個屬性:Height、Width和 Depth。
一般而言,類型解析規則要求在參考時,類型名稱必須完整。 例外狀況是在與目前型別相同的命名空間中定義型別時。 如果 區域 和 磁碟區 都位於相同的命名空間中,上述範例的運作方式就是撰寫的。
已實作的介面
類別定義也可以指定類別實作的介面清單。 您可以將介面指定為以逗號分隔的介面清單,以遵循 (選擇性) 基類。
在下列範例中,Area 類別會實作 IStringable 介面;和 Volume 類別會實作 IStringable 和假設 IEquatable 介面。
unsealed runtimeclass Area : Windows.Foundation.IStringable
{
Area(Int32 width, Int32 height);
Int32 Height;
Int32 Width;
}
runtimeclass Volume : Area, Windows.Foundation.IStringable, IEquatable
{
Volume(Int32 width, Int32 height, Int32 depth);
Int32 Depth;
}
在 MIDL 中,您不會在 類別上宣告介面的成員。 當然,您必須在實際實作上宣告和定義它們。
成員
類別的成員
下表顯示類別可以包含的成員類型。
成員種類 | 描述 |
---|---|
構造 函數 | 初始化類別實例或初始化類別本身所需的動作 |
性能 | 與讀取和寫入類別實例或類別本身之具名屬性相關聯的動作 |
方法 | 可由類別實例或類別本身執行的計算和動作 |
事件 | 可由類別實例引發的通知 |
構造 函數
MIDL 3.0 支援實例建構函式的宣告。 實例建構函式 是實作初始化類別實例所需的動作的方法。 建構函式可能不是靜態的。
建構函式會宣告為類似實例方法(但不含傳回型別),且名稱與包含類別相同。
實例建構函式可以多載。 例如,下列 Test 類別會宣告三個實例建構函式:其中一個沒有參數(預設 建構函式),一個採用 Int32 參數,另一個採用兩個 Double 參數(參數化 建構函式)。
runtimeclass Test
{
Test();
Test(Int32 x);
Test(Double x, Double y);
}
如需參數列表語法的詳細資訊,請參閱下列 方法。
會繼承實例屬性、方法和事件。 實例建構函式不會繼承 (但有一個例外狀況),而且類別沒有實際在類別中宣告的實例建構函式。 如果未提供類別的實例建構函式,則您無法直接具現化類別。 對於這類類別,您通常會有傳回 類別實例的處理站方法。
例外狀況是未密封的類別。 未密封的類別可以有一或多個受保護的建構函式。
性能
屬性 在概念上類似於欄位(例如 C# 欄位;或 MIDL 3.0 結構的欄位)。 屬性和欄位都是具有名稱和相關聯型別的成員。 不過,與欄位不同,屬性不代表儲存位置。 相反地,屬性 存取子,指定要在讀取或寫入屬性時執行的函式。
屬性的宣告方式就像結構欄位一樣,不同之處在於宣告的結尾為 get
關鍵詞和/或以分號結尾的分隔符 { 和 }之間寫入的 set
關鍵詞。
同時具有 get
關鍵詞和 set
關鍵字的屬性是 讀寫屬性。 只有 get
關鍵字的屬性是 唯讀屬性。 Windows 運行時間不支援唯寫屬性。
例如,先前所見 Area類別包含兩個名為 Height 和 Width的讀寫屬性。
unsealed runtimeclass Area
{
Int32 Height { get; set; };
Int32 Width; // get and set are implied if both are omitted.
}
Width 的宣告 省略大括弧和 get
和 set
關鍵詞。 遺漏表示屬性是讀寫的,而且語意上與依序提供 get
和 set
關鍵詞相同,get
,後面接著 set
。
此外,您只能指定 get
關鍵詞,以指出屬性是唯讀的。
// Read-only instance property returning mutable collection.
Windows.Foundation.Collections.IVector<Windows.UI.Color> Colors { get; };
Windows 運行時間不支援唯寫屬性。 但是,您只能指定 set
關鍵詞,將現有的唯讀屬性修改為讀寫屬性。 以這個版本的 Area 為例。
unsealed runtimeclass Area
{
...
Color SurfaceColor { get; };
}
如果您想要後續讓 SurfaceColor 屬性讀寫,而且不需要維持與先前 Area 定義的二進制相容性(例如,Area 類別是每次重新編譯的應用程式類型),則您只要將 set
關鍵詞新增至現有的 SurfaceColor 宣告即可。
unsealed runtimeclass Area
{
...
Color SurfaceColor { get; set; };
}
另一方面,如果您 確實 需要二進位穩定性(例如,Area 類別是您寄送給客戶的連結庫中的元件),則您無法將 set
關鍵詞新增至現有的屬性宣告。 這麼做會將二進位介面變更為類別。
在此情況下,請將 屬性 set
關鍵詞新增至類別結尾處屬性的其他定義,如下所示。
unsealed runtimeclass Area
{
...
Color SurfaceColor { get; };
...
Color SurfaceColor { set; };
}
編譯程式會產生唯寫屬性的錯誤。 但這不是這裡正在做的事情。 由於上述屬性宣告為唯讀,因此新增 set 關鍵詞不會宣告唯寫屬性,而是宣告讀寫屬性。
屬性的 Windows 執行時間實作是介面上的一或兩個存取子方法。 屬性宣告中 get 和 set 關鍵詞的順序會決定支援介面中 get 和 set 存取子方法的順序。
get
存取子會對應至屬性型別傳回值的無參數方法,也就是屬性 getter。
set
存取子會對應至具有名為 值之單一參數的方法,而且沒有傳回型別—屬性 setter。
因此,這兩個宣告會產生不同的二進位介面。
Color SurfaceColor { get; set; };
Color SurfaceColor { set; get; };
靜態和實例屬性
與方法類似,MIDL 3.0 同時支持實例屬性和靜態屬性。 靜態屬性會以前面加上 static
修飾詞來宣告,而實例屬性則宣告為不含它。
方法
方法 是實作可由 類別實例或類別本身執行之計算或動作的成員。 透過類別存取靜態方法
方法具有void
。
// Instance method with no return value.
void AddData(String data);
// Instance method *with* a return value.
Int32 GetDataSize();
// Instance method accepting/returning a runtime class.
// Notice that you don't say "&" nor "*" for reference types.
BasicClass MergeWith(BasicClass other);
// Asynchronous instance methods.
Windows.Foundation.IAsyncAction UpdateAsync();
Windows.Foundation.IAsyncOperation<Boolean> TrySaveAsync();
// Instance method that returns a value through a parameter.
Boolean TryParseInt16(String input, out Int16 value);
// Instance method that receives a reference to a value type.
Double CalculateArea(ref const Windows.Foundation.Rect value);
// Instance method accepting or returning a conformant array.
void SetBytes(UInt8[] bytes);
UInt8[] GetBytes();
// instance method that writes to a caller-provided conformant array
void ReadBytes(ref UInt8[] bytes);
方法的 簽章 在宣告方法的類別中必須是唯一的。 方法的簽章包含方法的名稱、其參數的類型,以及/或其參數的數目。 方法的簽章不包含傳回型別。
方法可見性修飾詞
當方法存在於衍生類別中時,方法 可能有兩個選擇性可見性修飾詞之一。
可覆寫的 修飾詞指出,這個方法可能由屬於子類別的方法(具有相同名稱和簽章)覆寫。
受保護的 修飾詞指出,這個方法只能由後續衍生類別中的成員存取。
方法多載
方法 多載 允許相同類別中的多個方法具有相同名稱,只要其參數數目不同(換句話說,方法 arity不同)。
runtimeclass Test
{
static void F();
static void F(Double x);
static void F(Double x, Double y);
}
注意
具有相同名稱的所有方法都應該 arity不同。 這是因為弱型別程式設計語言不支援依類型多載。
參數
參數 用來傳遞方法的值或變數參考。 參數 描述具有類型和名稱的插槽,以及選擇性的一些修飾詞關鍵詞。 自變數 是從 方法呼叫端傳遞給被呼叫者的實際值。
方法的參數會從叫用方法時指定的特定 自變數 取得其值。 在呼叫端和被呼叫端之間傳遞自變數的方式取決於參數的類型。 根據預設,所有參數都會 輸入參數,也就是只會從呼叫端封送處理到被呼叫者。 您可以新增修飾詞關鍵詞 ref
、ref const
和 out
,以修改呼叫端與被呼叫者之間封送處理的預設方向,並建立 輸出參數。 不過,並非所有關鍵詞都適用於所有參數類型有效;有效的組合如下所述。
重要
Common Language Runtime (CLR) 具有概念和修飾詞關鍵詞,可能類似於本節中所述的概念和修飾詞關鍵詞。 不過,實際上這些修飾詞並不相關,而且這些修飾詞的效果是 Windows 運行時間的設計與運作所特有。
實值型別會隱含地 輸入參數,而且根據預設,自變數的複本會從呼叫端傳遞至被呼叫者。 值參數可以使用
runtimeclass Test
{
static void Divide(Int32 x, Int32 y, out Int32 result, out Int32 remainder);
}
作為特殊的效能優化,結構類型(且沒有其他類型)通常會以值作為完整復本傳遞,可以透過不可變結構指標傳遞。 這是使用 ref const
(不是const ref
) 關鍵詞來達成,它會將結構參數標示為輸入參數,但會指示封送處理器將指標傳遞至結構記憶體,而不是傳遞結構的完整複本。 不過請注意,結構是不可變的;指標在概念上是 const 指標 。 沒有涉及拳擊。 例如,當接受與 Matrix4x4一樣大的值時,這是一個實用的選擇。
runtimeclass Test
{
static Boolean IsIdentity(ref const Windows.Foundation.Numerics.Matrix4x4 m);
}
參考型別也是隱含的輸入參數,這表示呼叫端負責配置物件,並將參考傳遞至該對象作為自變數;不過,由於 自變數是 對象的參考,因此呼叫端會在呼叫之後觀察到呼叫端對該物件的修改。 或者,可以使用 out
關鍵詞,將參考型別設為輸出參數。 在此情況下,角色會反轉;被呼叫者是配置 對象的物件,並將它傳回給呼叫端。 同樣地,ref
關鍵詞一般無法搭配參考型別使用(請參閱下面的例外狀況)。
runtimeclass Test
{
static void CreateObjectWithConfig(Config config, out MyClass newObject);
}
下表摘要說明值參數和參考參數封送處理關鍵詞的行為:
行為 | 配置者 | 關鍵詞 | 類型 | 言論 |
---|---|---|---|---|
輸入參數 | 訪客 | (無) | 所有類型 | 默認行為 |
ref const |
僅結構 | 效能優化 | ||
輸出參數 | 被呼叫者 | out |
所有類型 |
Windows 運行時間支援數位型別,其行為與參數有些不同。
陣列 是一種數據結構,其中包含一些循序儲存並透過索引存取的變數。 陣列中包含的變數,也稱為數位
MIDL 3.0 支援 單一維度陣列的宣告。
陣列參數 是參考型別,而且所有參考型別預設都是輸入參數。 在此情況下,呼叫端會將數位配置給被呼叫者,該陣列可以讀取其元素,但無法修改它們(只讀)。 這稱為 傳遞陣列 模式。 或者,將 ref
關鍵詞新增至 參數,即可使用 填滿陣列 模式;在該設定中,陣列仍由呼叫端配置,但在概念上是輸出參數,表示被呼叫者會填入陣列元素的值。 最後,最後一個模式是 接收數位,其中(如同所有輸出參考參數),被呼叫者在傳回給呼叫端之前,都會配置和初始化自變數。
runtimeclass Test
{
// Pass array pattern: read-only array from caller to callee
void PassArray(Int32[] values);
// Fill array pattern: caller allocates array for callee to fill
void FillArray(ref Int32[] values);
// Receive array pattern: callee allocates and fill an array returned to caller
void ReceiveArray(out Int32[] values);
}
下表摘要說明陣列及其元素的行為:
數位模式 | 關鍵詞 | 配置者 | 依被呼叫者存取的專案 |
---|---|---|---|
“Pass array” | (無) | 訪客 | 唯讀 |
“Fill array” | ref |
訪客 | 僅限寫入 |
“Receive array” | out |
被呼叫者 | 讀寫 |
如需使用 C 樣式陣列參數的詳細資訊,也稱為一致性陣列,搭配 C++/WinRT,請參閱 陣列參數。
靜態和實例方法
以前置詞 static
宣告的方法是靜態方法 。 靜態方法無法存取特定實例,因此只能直接存取 類別的其他靜態成員。
沒有 static
修飾詞宣告的方法是 實體方法。
實例方法可以存取特定實例,而且可以存取 類別的靜態和實例成員。
下列 Entity 類別同時具有靜態和實例成員。
runtimeclass Entity
{
Int32 SerialNo { get; };
static Int32 GetNextSerialNo();
static void SetNextSerialNo(Int32 value);
}
每個 實體 實例都包含它自己的序號(大概是此處未顯示的某些其他資訊)。 在內部,Entity 建構函式(就像實例方法一樣)會使用下一個可用的序號來初始化新的實例。
SerialNo 屬性可讓您存取您叫用 屬性之實例的序號,取得 方法。
可覆寫和受保護的方法
Windows 執行時間類型中的所有方法實際上都是虛擬的。 叫用虛擬方法時,該叫用的實例 運行時間類型,該叫用會決定要叫用的實際方法實作。
方法可以在衍生類別中 覆寫。 當實例方法宣告包含 overridable
修飾詞時,衍生類別可以覆寫方法。 衍生類別是否確實覆寫可覆寫的基類方法是由實作所決定:它不存在於元數據中。 如果衍生類別在基類中重新宣告方法,則會宣告與衍生類別方法並列的新方法,而不是覆寫它。
當實例方法宣告包含 protected
修飾詞時,只有衍生類別才能看到方法。
事件
事件 宣告是指定類別為事件來源的成員。 這類事件來源會將通知提供給任何實作委派的收件者(具有特定簽章的方法)。
您可以使用 event
關鍵詞來宣告事件,後面接著委派類型名稱(描述必要的方法簽章),後面接著事件的名稱。 以下是從平臺使用現有委派類型的範例事件。
runtimeclass Area
{
...
event Windows.UI.Xaml.WindowSizeChangedEventHandler SizeChanged;
...
}
事件宣告會隱含地將兩個方法新增至 類別:新增 方法,用戶端會呼叫此方法將事件處理程式新增至來源,而 移除 方法,用戶端會呼叫此方法來移除先前新增的事件處理程式。 以下是更多範例。
// Instance event with no meaningful payload.
event Windows.Foundation.TypedEventHandler<BasicClass, Object> Changed;
// Instance event with event parameters.
event Windows.Foundation.TypedEventHandler<BasicClass, BasicClassSaveCompletedEventArgs> SaveCompleted;
// Static event with no meaningful payload.
static event Windows.Foundation.EventHandler<Object> ResetOccurred;
// Static event with event parameters.
static event Windows.Foundation.EventHandler<BasicClassDeviceAddedEventArgs> DeviceAdded;
依照慣例,一律會將兩個參數傳遞至 Windows 運行時間事件處理程式:傳送者的身分識別,以及事件自變數物件。 傳送者是引發事件的物件,或靜態事件的 Null。 如果事件沒有有意義的承載,則事件自變數是 Object 其值為 null。
代表
委派類型 指定具有特定參數清單和傳回型別的方法。 事件的單一實例可以包含其委派型別實例的任意數目參考。 宣告與一般成員方法的宣告類似,不同之處在於它存在於運行時間類別之外,而且前面會加上 delegate
關鍵詞。
委派可讓您將方法視為可指派給變數並傳遞為參數的實體。 委派類似於某些其他語言中找到的函式指標概念。 但是,與函式指標不同,委派是面向物件且類型安全。
如果我們不想從平臺使用 WindowSizeChangedEventHandler 委派類型,我們可以定義自己的委派類型。
delegate void SizeChangedHandler(Object sender, Windows.UI.Core.WindowSizeChangedEventArgs args);
SizeChangedHandler 委派類型的實例可以參考接受兩個自變數的任何方法(Object和 WindowSizeChangedEventArgs),並傳回 void。 在討論 結構之後,您也將能夠將 WindowSizeChangedEventArgs 參數取代為您自己的 事件自變數 類型。
委派的有趣且實用的屬性是,它不知道或關心所參考之方法的類別;重要的是,參考的方法具有與委派相同的參數和傳回型別。
您可以選擇性地使用 [uuid(...)]
將委派宣告屬性化。
另請參閱傳回 HRESULT的委派
結構
結構 是可以包含數據成員 (fields) 的數據結構。 但是,不同於類別,結構是實值型別。
結構特別適用於具有值語意的小型數據結構。 複數或座標系統中的點是結構的良好範例。 使用結構而非小型數據結構的類別,可能會對應用程式執行的記憶體配置數目產生很大差異。
讓我們使用範例來對比類別和結構。 以下是第一個 Point 版本,做為 類別。
runtimeclass Point
{
Point(Int32 x, Int32 y);
Int32 x;
Int32 y;
}
此 C# 程式會建立並初始化 100 個 Point實例的陣列。 使用 Point 實作為類別,會具現化 101 個不同的物件:一個用於數位物件本身;和 100 個 Point 元素各一個。
class Test
{
static Test()
{
Point[] points = new Point[100];
for (Int32 i = 0; i < 100; ++i) points[i] = new Point(i, i);
}
}
更有效能的替代方法是讓 Point 結構,而不是類別。
struct Point
{
Int32 x;
Int32 y;
};
現在,只會具現化一個物件,也就是數位物件本身。 Point 元素會以行方式儲存在陣列內;處理器快取能夠用來產生強大效果的記憶體排列。
變更結構是二進位中斷性變更。 因此,在引進 Windows 本身時,不會改變實作為 Windows 本身一部分的結構。
介面
介面 定義可由類別實作的合約。 介面可以包含方法、屬性和事件,就像類別一樣。
不同於類別,介面不會提供其所定義成員的實作。 它只會指定任何實作 介面的類別必須提供的成員。
介面 需要 實作 介面的類別,以實作其他介面。 在下列範例中,IComboBox
interface IControl
{
void Paint();
}
interface ITextBox requires IControl
{
void SetText(String text);
}
interface IListBox requires IControl
{
void SetItems(String[] items);
}
interface IComboBox requires ITextBox, IListBox
{
...
}
類別可以實作零個或多個介面。 在下一個範例中,EditBox 類別 會實作 IControl 和 IDataBound。
interface IDataBound
{
void Bind(Binder b);
}
runtimeclass EditBox : IControl, IDataBound
{
}
針對 Windows 平臺中的 Windows 執行時間類型,如果取用這些類型的開發人員預期會實作介面,則會定義介面。 定義介面的另一個使用案例是當多個運行時間類別實作 介面時,取用這些運行時間類別的開發人員會透過該通用介面一般存取不同類型的物件(因此多型)。
注意
請考慮在 MIDL 3.0 中使用 requires
關鍵詞兩次。 這可能會導致混亂的設計,尤其是在將版本設定納入考慮時。
列舉
列舉類型(或列舉型別或列舉型別)是具有一組具名常數的相異實值型別。 下列範例會定義並使用名為 Color 的列舉類型,並具有三個常數值:Red、Green和 Blue。
enum Color
{
Red,
Green,
Blue, // Trailing comma is optional, but recommended to make future changes easier.
};
每個列舉類型都有一個對應的整數類型,稱為列舉型別
Windows 運行時間支援兩種列舉:一般 列舉,以及 旗標 列舉。 一般類型的列舉表示一組獨佔值;而其中一個旗標種類代表一組布爾值。 若要啟用旗標列舉的位運算元,MIDL 3.0 編譯程式會產生C++運算元多載。
旗標列舉已套用 [flags]
屬性。 在這裡情況下,列舉的基礎類型 UInt32。 當 [flags]
屬性不存在時(一般列舉),列舉的基礎類型會 Int32。 無法將列舉宣告為任何其他類型。
[flags]
enum SetOfBooleanValues
{
None = 0x00000000,
Value1 = 0x00000001,
Value2 = 0x00000002,
Value3 = 0x00000004,
};
列舉類型的儲存格式和可能值的範圍取決於其基礎類型。 列舉類型可以接受的值集不受其宣告的列舉成員所限制。
下列範例會定義名為 Alignment的列舉類型,其基礎類型為 Int32。
enum Alignment
{
Left = -1,
Center = 0,
Right = 1
};
如同 C 和 C++ 也是如此,MIDL 3.0 列舉可以包含常數運算式,指定成員的值(如上所示)。 每個列舉成員的常數值必須位於列舉的基礎類型範圍內。 當列舉成員宣告未明確指定值時,就會將成員指定為零值(如果它是列舉類型中的第一個成員),或前面列舉成員加上一個的文字值。
下列範例會定義名為 Permissions的列舉類型,其基礎類型為 UInt32。
[flags]
enum Permissions
{
None = 0x0000,
Camera = 0x0001,
Microphone = 0x0002
};
屬性
MIDL 3.0 原始程式碼中的類型、成員和其他實體支援控制其行為特定層面的修飾詞。 例如,使用 protected
存取修飾詞來控制方法的存取範圍。 MIDL 3.0 會將這項功能一般化,讓使用者定義的宣告式資訊類型可以附加至程序實體,並在運行時間從元數據擷取。
程式藉由定義和使用 屬性來指定這個額外的宣告式資訊。
下一個範例會定義 HelpAttribute 屬性,該屬性可以放在程式實體上,以提供其相關聯文件的連結。 如您所見,屬性基本上是結構類型,因此它沒有建構函式,而且只包含數據成員。
[attributeusage(target_runtimeclass, target_event, target_method, target_property)]
attribute HelpAttribute
{
String ClassUri;
String MemberTopic;
}
屬性可以藉由在相關聯宣告之前提供其名稱以及方括弧內的任何自變數來套用。 如果屬性的名稱以 Attribute 結尾,則可以在參考屬性時省略該部分的名稱。 例如,HelpAttribute 屬性可以使用如下。
[Help("https://docs.contoso.com/.../BookSku", "BookSku class")]
runtimeclass BookSku : Windows.UI.Xaml.Data.INotifyPropertyChanged
{
[Help("https://docs.contoso.com/.../BookSku_Title", "Title method")]
String Title;
}
您可以使用屬性後面的範圍區塊,將相同的屬性套用至多個宣告。 也就是說,屬性緊接著套用屬性之宣告周圍的大括弧。
runtimeclass Widget
{
[Help("https://docs.contoso.com/.../Widget", "Widget members")]
{
void Display(String text);
void Print();
Single Rate;
}
}
實作為 Windows 本身一部分的屬性通常位於 windows.Foundation 命名空間
如第一個範例所示,您會在屬性定義上使用 [attributeusage(<target>)]
屬性。 有效的目標值為 target_all
、target_delegate
、target_enum
、target_event
、target_field
、target_interface
、target_method
、target_parameter
、target_property
、target_runtimeclass
和 target_struct
。 您可以在括弧中包含多個目標,並以逗號分隔。
您可以套用至屬性的其他屬性是 [allowmultiple]
與 [attributename("<name>")]
。
參數化類型
下列範例會產生 錯誤MIDL2025:[msg]語法錯誤 [context]:預期 >,或接近 “>>”。
Windows.Foundation.IAsyncOperation<Windows.Foundation.Collections.IVector<String>> RetrieveCollectionAsync();
相反地,在兩個 >
字元之間插入空格,讓範本結尾字元組不會誤譯為右移運算元。
Windows.Foundation.IAsyncOperation<Windows.Foundation.Collections.IVector<String> > RetrieveCollectionAsync();
下列範例會產生 錯誤MIDL2025:[msg]語法錯誤 [context]:預期 >,或接近 “[”。 這是因為使用數位做為參數化介面的參數類型自變數無效。
Windows.Foundation.IAsyncOperation<Int32[]> RetrieveArrayAsync();
如需解決方案,請參閱 以異步方式傳回數位列。