Microsoft 介面定義語言 3.0 的簡介

Microsoft Interface Definition Language (MIDL) 3.0 是簡化的新式語法,用於在介面定義語言 (IDL) 檔案中定義Windows 執行階段類型, (.idl 檔案) 。 這個新語法會熟悉 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 3.0,您需要 Windows SDK 10.0.17134.0 版 (Windows 10版本 1803) (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 介面和 coclass 的增強功能。

更新的 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 執行時間類別。 用於 C++/WinRT 的標頭,適用于內建Windows 執行階段 API 的標頭是 資料夾 %WindowsSdkDir%Include<WindowsTargetPlatformVersion>\cppwinrt\winrt 內的 SDK 一部分。

MIDL 3.0 的使用案例

一般而言,所有Windows 執行階段 API 都是設計成適用于所有Windows 執行階段語言投影。 這一部分是藉由選擇在Windows 執行階段 API 之間獨佔傳遞Windows 執行階段類型來完成。 雖然這是將原始 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

如果您想要公開給 C++ 以外的Windows 執行階段語言投影的 COM元件有一些特性或功能,您可以建立 C++ Windows 執行階段元件, (WRC) 直接建立和使用 COM 元件 (,例如 DirectX,例如) ,並公開其部分特性和功能的複寫,格式為Windows 執行階段只接受並傳回Windows 執行階段類型的 API 介面。 然後,您可以從以任何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) 、一些透過Title) (AuthorName的唯讀屬性,以及兩個名為EqualsApplyDiscount的方法。 請注意 使用 Single 類型,而不是 float。 而 該字串 具有大寫 「S」。

提示

Visual Studio 透過 C++/WinRT Visual Studio 擴充功能 (VSIX) ,提供編譯 MIDL 3.0 的最佳體驗。 請參閱 C++/WinRT 和 VSIX 的 Visual Studio 支援

但您也可以從命令列編譯 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 取用者將參考的檔案。

在此情況下, BookSkuBookstore 命名空間中唯一的執行時間類別,因此我們已儲存步驟,並只 .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,您無法針對這些類型使用別名。

類別 描述
值類型 簡單型別 帶正負號整數: Int16Int32Int64
不帶正負號整數: UInt8UInt16UInt32UInt64
Unicode 字元: Char (代表 UTF-16LE;16 位 Unicode 程式碼單元)
Unicode 字串: 字串
IEEE 浮點數:單一雙精度浮點數
布林值: 布林值
128 位 UUID: Guid
列舉型別 表單列舉 E {...}的使用者定義類型
結構型別 表單結構 S {...}的使用者定義類型
可為 Null 的型別 具有 Null 值之所有其他實值型別的延伸
參考型別 類別類型 所有其他類型的最終基類: Object
表單執行時間類別 C {...}的使用者定義類型
介面型別 表單介面 I {...}的使用者定義類型
委派型別 表單委派 < returnType > D (...) 的使用者定義類型

七個整數類型支援 8 位無符號資料;和 16 位、32 位和 64 位值,格式為帶正負號或不帶正負號。

這兩個浮點類型 SingleDouble分別代表使用 32 位單精確度和 64 位雙精確度 IEEE 754 格式的資料。

MIDL 3.0 的 布林 數值型別代表布林值;或 truefalse

MIDL 3.0 中的字元和字串包含 Unicode 字元。 Char類型代表 UTF-16LE 程式碼單位;和String類型代表 UTF-16LE 程式碼單位的序列。

下表摘要說明 MIDL 3.0 的數數值型別。

類別 Bits 類型 範圍/有效位數
帶正負號整數 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 Single 1.5 × 10-45 到 3.4 × 1038,7 位數有效位數
64 Double 5.0 × 10-324 至 1.7 × 10308,15 位數有效位數

MIDL 3.0 原始程式檔會使用類型定義來建立新的類型。 型別定義會指定新型別的名稱和成員。 這些 MIDL 3.0 類型類別是使用者可定義。

  • 屬性類型,
  • 結構類型,
  • 介面類別型,
  • runtimeclass 類型,
  • 委派類型, 和
  • 列舉類型。

屬性類型會定義可套用至其他類型定義的Windows 執行階段屬性。 屬性會提供有關套用屬性之型別的中繼資料。

結構類型會定義包含資料成員 (欄位) Windows 執行階段結構。 結構是實值型別,而且不需要堆積配置。 結構類型的資料成員必須是實值型別或可為 Null 的類型。 結構類型不支援繼承。

介面類型會定義Windows 執行階段介面,這是一組具名的函式成員。 介面可以指定介面的實作也必須實作一或多個必要的額外 () 介面。 每個介面類別型都會直接衍生自 Windows 執行階段IInspectable介面。

runtimeclass類型會定義執行時間類別) (類別Windows 執行階段類別。 執行時間類別包含可以是屬性、方法和事件的成員。

委派類型會定義Windows 執行階段委派,此委派表示具有特定參數清單和傳回型別之方法的參考。 委派可讓您將方法視為可當做參數傳遞的實體。 委派類似于某些其他語言中找到的函式指標概念。 不同于函式指標,委派是物件導向,且型別安全。

列舉類型是具有具名常數的相異類型。 每個列舉類型都有隱含的基礎類型; Int32UInt32。 列舉類型的值集與基礎類型的值集合相同。

MIDL 3.0 支援三個額外的類型類別。

  • 單一維度陣列類型,
  • 可為 Null 的實值型別和
  • 物件類型。

您不需要先宣告單一維度陣列,才能使用它。 而陣列型別的建構方法,是在型別名稱之後加上方括弧。 例如, Int32[]Int32的單維陣列。

同樣地, 不可為 Null 的實值型別也不需要先定義,才能使用。 除了String) 以外,每個不可為 Null 的實數值型別T (,都有對應的可為 Null 的類型Windows.Foundation.IReference < T >,可保留額外的值 null 。 例如,Windows.Foundation.IReference < Int32 >是可以保存任何 32 位整數的類型,或值 null 。 另請參閱IReference < T >

最後,MIDL 3.0 支援Object類型,其對應至IInspectable介面Windows 執行階段。 介面runtimeclass參考型別在概念上衍生自Object類型;委派不會。

列舉值中的運算式

使用 MIDL 3.0 時,您只能在列舉類型具名常數的值定義中使用 運算式 ;換句話說,在列舉初始化運算式中。

運算式是從 運算元運算子建構而來。 運算式中的運算子會指出要套用至運算元的作業。 運算子的範例包括 +、-、*、/和 new 。 運算元範例包括常值、欄位、區域變數及運算式。

當運算式包含多個運算子時,運算子的「優先順序」會控制個別運算子的評估順序。 例如,運算式 x + y * z 會評估為 x + (y * z) ,因為 * 運算子的優先順序高於 + 運算子。 邏輯作業的優先順序低於位作業。

下表摘要說明 MIDL 3.0 的運算子,列出優先順序從最高到最低的運算子類別。 相同類別中的運算子擁有相同優先順序。

類別 運算式 描述
Primary x++ 後置遞增
x-- 後置遞減
一元 +x 身分識別
-X 否定
!x 邏輯否定
~x 位元否定
++x 前置遞增
--x 前置遞減
乘法 x * y 乘法
x / y 部門
x % y 餘數
加法 x + y 加法、字串串連、委派組合
x – y 減法、委派移除
Shift 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; };
}

這會定義名為Area的新Windows 執行階段類別,其建構函式採用兩個Int32參數、兩個名為HeightWidthInt32讀寫屬性,以及名為NumberOfAreas的靜態唯讀屬性。

根據預設,執行時間類別是密封的,而且不允許衍生自它。 請參閱 基類

若要將 XAML 系結至檢視模型,必須在 MIDL 中定義檢視模型執行時間類別。 如需詳細資訊,請參閱 XAML 控制項;繫結至 C++/WinRT 屬性

您可以宣告類別不支援任何實例 (,因此在執行時間類別定義前面 static 加上 關鍵字,因此必須只包含靜態成員) 。 將非靜態成員新增至 類別,然後會導致編譯錯誤。

static runtimeclass Area
{
    static Int32 NumberOfAreas { get; };
}

靜態類別與空類別不同。 另請參閱 空白類別

您可以藉由將執行時間類別定義前面加上 partial 關鍵字,來指出類別定義不完整。 編譯器遇到的所有部分類別定義都會合並成單一執行時間類別。 這項功能主要適用于 XAML 撰寫案例,其中部分類別是機器產生的。

修飾詞 意義
static 類別沒有實例。 因此,只允許靜態成員。
partial 類別定義不完整。

如需進階修飾詞 ,請參閱組合和啟用

成員存取修飾詞

由於 MIDL 3.0 是描述Windows 執行階段類型公用介面的定義語言,因此不需要明確語法來宣告成員的公用存取範圍。 所有成員都是隱含公開的。 這就是 MIDL 3.0 不需要或允許 (public 有效備援) 關鍵字的原因。

基底類別

類別定義可以依照類別名稱和型別參數的冒號和基類名稱來指定基類。 省略基類規格與從IInspectable) 衍生自Object (類型相同。

注意

您的檢視模型類別事實上,您在應用程式中定義的任何執行時間類別,都不需要衍生自基類。

您在衍生自基類之應用程式中定義的任何運行時間類別稱為可撰寫類別。 而且有以可組合的類別為主的條件約束。 若要讓應用程式通過 Visual Studio 和 Microsoft Store 所使用的 Windows 應用程式認證套件測試來驗證提交 (因而讓應用程式成功擷取到 Microsoft Store 中),可組合的類別必須最終衍生自 Windows 基底類別。 這表示位於繼承階層根目錄的類別必須是源自 Windows.* 命名空間的類型。

如需詳細資訊,請參閱 XAML 控制項;繫結至 C++/WinRT 屬性

在下一個範例中, 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;
}

注意

在這裡, 區域磁片區 是在相同的原始程式檔中定義。 如需優缺點的討論,請參閱 將執行時間類別分解成 Midl 檔案, (.idl)

類別會繼承其基底類別的成員。 繼承表示類別會隱含包含其基類的所有成員,但基類的建構函式 () 除外。 衍生類別可以在其繼承的成員中新增新的成員,但無法移除所繼承成員的定義。

在上一個範例中,Volume會繼承AreaHeightWidth屬性。 因此,每個 Volume 實例都包含三個屬性: HeightWidthDepth

一般而言,類型解析規則會在參考時要求類型名稱為完整。 例外狀況是在與目前類型相同的命名空間中定義型別時。 如果 AreaVolume 都位於相同的命名空間中,上述範例的運作方式就如同撰寫一樣。

已實作的介面

類別定義也可以指定類別實作的介面清單。 您可以在 (選擇性) 基類之後,將介面指定為逗號分隔的介面清單。

在下列範例中, 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);
}

如需參數清單語法的詳細資訊,請參閱下面的 方法

繼承實例屬性、方法和事件。 實例建構函式不會繼承 (,但有一個例外狀況) ,而且類別沒有在 類別中實際宣告的實例建構函式。 如果未提供類別的實例建構函式,則您無法直接具現化類別。 針對這類類別,您通常會在傳回 類別實例的其他位置使用 Factory 方法。

例外狀況是未密封的類別。 未密封類別可以有一或多個受保護的建構函式。

屬性

屬性 在概念上類似于欄位,例如 C# 欄位 (;或 MIDL 3.0 結構的欄位) 。 屬性和欄位都是具有名稱和相關聯類型的成員。 不過,不同于欄位,屬性不會表示儲存位置。 相反地,屬性具有 存取子 ,可指定要在讀取或寫入屬性時執行的函式。

屬性的宣告方式就像結構欄位一樣,不同之處在于宣告是以關鍵字和/或 set 以分隔符號 { 和 } 撰寫的關鍵字結尾 get ,並以分號結尾。

同時具有 get 關鍵字和 set 關鍵字的屬性是 讀寫屬性。 只有 get 關鍵字的屬性是 唯讀屬性。 Windows 執行階段不支援唯寫屬性。

例如,先前所見 的 Area類別包含兩個名為 HeightWidth的讀寫屬性。

unsealed runtimeclass Area
{
    Int32 Height { get; set; };
    Int32 Width; // get and set are implied if both are omitted.
}

Width的宣告會省略大括弧和 getset 關鍵字。 省略表示屬性為讀寫,且語意上與依該順序提供 getset 關鍵字相同, 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 關鍵字新增至現有的屬性宣告。 這麼做會將二進位介面變更為類別。

在此情況下,請將 property 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。 這是因為弱式型別程式設計語言不支援依類型多載。

參數

參數 可用來傳遞方法的值或變數參考。 參數描述具有類型和名稱的位置,並選擇性地描述一些修飾詞關鍵字。 引數是從 方法的呼叫端傳遞至被呼叫端的實際值。

方法的參數會從叫用方法時所指定的特定 引數 取得其值。 在呼叫端與被呼叫端之間傳遞引數的方式取決於 參數的類型。 根據預設,所有參數都是 輸入參數,也就是只會從呼叫端封送處理到被呼叫端。 您可以新增 修飾詞關鍵字 refref constout 來修改呼叫端與被呼叫端之間封送處理的預設方向,並建立 輸出參數。 不過,並非所有關鍵詞都適用于所有參數類型有效;有效組合詳述如下。

重要

Common Language Runtime (CLR) 具有可能類似于本節中所述的概念和修飾詞關鍵字。 不過,實際上這些修飾詞不相關,而且這些修飾詞的效果專屬於Windows 執行階段的設計與運作。

實值型別是隱含 輸入參數,而且根據預設,引數的複本會從呼叫端傳遞至被呼叫端。 值參數可以使用 關鍵字轉換成 輸出參數out ;在此情況下,引數只會從被呼叫端封送處理回呼叫端。

runtimeclass Test
{
    static void Divide(Int32 x, Int32 y, out Int32 result, out Int32 remainder);
}

作為特殊的效能優化,結構類型 (,而且沒有其他類型) ,通常以值作為完整複本傳遞,可以透過指標傳遞至不可變的結構。 這可透過 ref const (notconst ref) 關鍵字來達成,它會將結構參數標示為輸入參數,但會指示封送處理器將指標傳遞至結構的儲存體,而不是傳遞結構的完整複本。 不過請注意,結構是不可變的;指標在概念上是 const 指標。 未涉及 Boxing。 例如,接受大小為 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 呼叫者 唯寫
「接收陣列」 out 被呼叫端 讀寫

如需搭配 C++/WinRT 使用 C 樣式陣列參數的詳細資訊,請參閱 陣列參數

靜態和執行個體方法

static 修飾詞前置詞宣告的方法是 靜態方法。 靜態方法無法存取特定實例,因此只能直接存取 類別的其他靜態成員。

在沒有修飾詞的情況下 static 宣告的方法是 實例方法。 實例方法可以存取特定實例,而且可以存取 類別的靜態和實例成員。

下列 Entity 類別同時具有靜態和實例成員。

runtimeclass Entity
{
    Int32 SerialNo { get; };
    static Int32 GetNextSerialNo();
    static void SetNextSerialNo(Int32 value);
}

每個 實體 實例都包含自己的序號 (,而且可能有些其他資訊未在此顯示) 。 在內部, Entity 建構函式 (就像實例方法) 使用下一個可用的序號來初始化新的實例。

SerialNo屬性可讓您存取叫用屬性 get方法之實例的序號。

GetNextSerialNoSetNextSerialNo靜態方法可以存取Entity類別的內部下一個可用序號靜態成員。

可覆寫和受保護的方法

Windows 執行階段類型中的所有方法實際上都是虛擬的。 叫用虛擬方法時,叫用所針對之執行個體的「執行階段型別」會決定要叫用的實際方法實作。

方法可以在衍生類別中 覆寫 。 當實例方法宣告包含 overridable 修飾詞時,衍生類別可以覆寫方法。 衍生類別是否確實覆寫可覆寫的基類方法是由 實作所決定;它不存在於中繼資料中。 如果衍生類別重新宣告基類中的方法,則會宣告與衍生類別方法一起的新方法,而不是覆寫它。

當實例方法宣告包含 protected 修飾詞時,只有衍生類別才能看到方法。

事件

事件宣告是指定類別為事件來源的成員。 這類事件來源會將通知提供給任何實作委派的收件者, (具有特定簽章的方法) 。

您可以使用 關鍵字宣告事件 event ,後面接著委派類型名稱 (,其中描述必要的方法簽章) ,後面接著事件的名稱。 以下是從平臺使用現有委派類型的範例事件。

runtimeclass Area
{
    ...
    event Windows.UI.Xaml.WindowSizeChangedEventHandler SizeChanged;
    ...
}

事件宣告會隱含地將兩個方法新增至 類別: add 方法,用戶端會呼叫此方法以將事件處理常式新增至來源,以及 移除 方法,而用戶端會呼叫以移除先前新增的事件處理常式。 以下是更多範例。

// 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。 如果事件沒有有意義的承載,則事件引數是 值為 Null 的 Object

委派

委派類型會指定具有特定參數清單和傳回型別的方法。 事件的單一實例可以包含其委派型別實例的任意數目參考。 宣告類似于一般成員方法的宣告,不同之處在于它存在於執行時間類別之外,而且前面會加上 delegate 關鍵字。

委派可讓您將方法視為可指派給變數並以參數傳遞的實體。 委派類似于某些其他語言中找到的函式指標概念。 但是,不同于函式指標,委派是物件導向且型別安全。

如果我們不想從平臺使用 WindowSizeChangedEventHandler 委派類型,我們可以定義自己的委派類型。

delegate void SizeChangedHandler(Object sender, Windows.UI.Core.WindowSizeChangedEventArgs args);

SizeChangedHandler委派類型的實例可以參考任何接受物件 (引數的方法,以及WindowSizeChangedEventArgs) ,並傳回 void。 在討論 結構之後,您也可以將 WindowSizeChangedEventArgs 參數取代為您自己的 事件自 變數類型。

委派的有趣且實用屬性是它不知道或關心所參考方法的類別;重要的是,參考的方法具有與委派相同的參數和傳回型別。

您可以選擇性地使用 [uuid(...)] 屬性化委派宣告。

另請參閱 傳回 HRESULT 的委派

結構

結構是資料結構,可包含資料成員 (欄位) 。 但是,不同于類別,結構是實值型別。

結構特別適用於含有實值語意的小型資料結構。 複數或座標系統中的點是結構的良好範例。 使用結構而非小型資料結構的類別,可能會對應用程式執行的記憶體配置數目產生很大差異。

讓我們使用範例來對比類別和結構。 以下是 Point 第一版作為 類別的版本。

runtimeclass Point
{
    Point(Int32 x, Int32 y);
    Int32 x;
    Int32 y;
}

此 C# 程式會建立並初始化 Point 100 個實例的陣列。 將 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 本身一部分的結構就不會改變。

介面

介面會定義可由類別實作的合約。 介面可以包含方法、屬性和事件,就像類別一樣。

不同于類別,介面不會提供其所定義成員的實作。 它只會指定實作 介面的任何類別必須提供的成員。

介面 可能需要實 作 介面的類別,才能同時實作其他介面。 在下列範例中, IComboBox 介面需要實作 IComboBox的任何類別,也會同時實作 ITextBoxIListBox。 此外,實作 IComboBox 的類別也必須實作 IControl。 這是因為 ITextBoxIListBox 都需要

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 類別會同時實作 IControlIDataBound

interface IDataBound
{
    void Bind(Binder b);
}

runtimeclass EditBox : IControl, IDataBound
{
}

對於 Windows 平臺中的Windows 執行階段類型,如果取用這些類型的開發人員預期會實作介面,就會定義介面。 定義介面的另一個使用案例是,當多個執行時間類別實作 介面時,取用這些執行時間類別的開發人員會存取不同類型的物件一般 (,因此透過該通用介面以多型方式) 。

注意

請考慮在 MIDL 3.0 中使用 requires 關鍵字兩次。 這可能會導致混亂的設計,特別是在將版本設定納入考慮時。

列舉

列舉類型 (或列舉類型,或列舉) 是具有一組具名常數的不同實值型別。 下列範例會定義並使用名為 Color 的列舉類型,其中包含三個常數值: 紅色綠色藍色

enum Color
{
    Red,
    Green,
    Blue, // Trailing comma is optional, but recommended to make future changes easier.
};

每個列舉類型都有對應的整數型別,稱為列舉 型別的基礎型 別。 列舉的基礎類型為 Int32UInt32

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_enumtarget_methodtarget_eventtarget_interfacetarget_fieldtarget_parameter 、、 target_propertytarget_runtimeclass 和 。 target_structtarget_delegatetarget_all 您可以在括弧中包含多個目標,並以逗號分隔。

您可以套用至屬性的其他屬性為 [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();

如需解決方案,請參閱 以非同步方式傳回陣列