轉型 (C++/CX)
四個不同的轉型運算子適用於 Windows 執行階段 類型:static_cast 運算子、dynamic_cast運算符、safe_cast運算符和 reinterpret_cast 運算元。 static_cast
safe_cast無法執行轉換時擲回例外狀況; static_cast 運算子也會執行編譯時間類型檢查。 如果dynamic_cast
無法轉換類型,則會傳回 nullptr
。 雖然 reinterpret_cast
會傳回非 null 值,但是可能無效。 因此,我們建議您不要使用 reinterpret_cast
,除非您知道轉型成功。 此外,建議您不要在 C++/CX 程式代碼中使用 C 樣式轉換,因為它們與 reinterpret_cast
相同。
編譯器和執行階段也會執行隱含轉型,例如,當實值類型或內建類型當做引數傳遞給參數類型為 Object^
的方法時,所進行的 boxing 作業。 理論上,隱含轉型不應該在執行階段造成例外狀況。如果編譯器無法執行隱含轉換,則會在編譯時期引發錯誤。
Windows 執行階段 是 COM 的抽象概念,它會使用 HRESULT 錯誤碼,而不是例外狀況。 一般而言, Platform::InvalidCastException 表示 E_NOINTERFACE 的低階 COM 錯誤。
static_cast
編譯時期會檢查 static_cast
,以判斷兩個類型之間是否有繼承關係。 如果這兩個類型不相關,則轉型會造成編譯器錯誤。
在 ref 類別上執行 static_cast
,會導致執行階段檢查。 ref 類別中的 static_cast
雖然通過編譯時期驗證,但是仍然可能會在執行階段失敗,在此情況下會擲回 Platform::InvalidCastException
。 一般而言,您不必處理這些例外狀況,因為這些例外狀況幾乎都表示可在開發和測試階段排除的程式設計錯誤。
如果程式碼明確宣告兩個類型之間的關聯性,因此您確定轉型應該會成功,請使用 static_cast
。
interface class A{};
public ref class Class1 sealed : A { };
// ...
A^ obj = ref new Class1(); // Class1 is an A
// You know obj is a Class1. The compiler verifies that this is possible, and in C++/CX a run-time check is also performed.
Class1^ c = static_cast<Class1^>(obj);
safe_cast
safe_cast運算符是 Windows 執行階段的一部分。 它會執行執行階段類型檢查,並在轉換失敗時擲回 Platform::InvalidCastException
。 當運行時間失敗指出例外狀況時,請使用 safe_cast 。 safe_cast的主要目的是協助識別開發與測試階段期間的程式設計錯誤。 您不必處理此例外狀況,因為未處理的例外狀況本身會識別失敗點。
如果程式碼未宣告關聯性,但是您確定轉型應該會成功,請使用 safe_cast。
// A and B are not related
interface class A{};
interface class B{};
public ref class Class1 sealed : A, B { };
// ...
A^ obj = ref new Class1();
// You know that obj's backing type implements A and B, but
// the compiler can't tell this by comparing A and B. The run-time type check succeeds.
B^ obj2 = safe_cast<B^>(obj);
dynamic_cast
dynamic_cast
當您將物件(更具體來說為 hat^)轉換成更衍生的類型時,您會預期目標物件有時nullptr
可能是 或轉換可能會失敗,而您想要將該條件當作一般程式代碼路徑來處理,而不是例外狀況。 例如,在 空白應用程式 (通用 Windows) 專案範本中, OnLaunched
app.xaml.cpp 中的 方法會使用 dynamic_cast
來測試應用程式視窗是否有內容。 如果沒有內容,則不是錯誤;這是預期的條件。 Windows::Current::Content
是 Windows::UI::XAML::UIElement
並且會轉換為 Windows::UI.XAML::Controls::Frame
,這在繼承階層架構中是屬於衍生程度較高的類型。
void App::OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs^ args)
{
auto rootFrame = dynamic_cast<Frame^>(Window::Current->Content);
// Do not repeat app initialization when the window already has content,
// just ensure that the window is active
if (rootFrame == nullptr)
{
// Create a Frame to act as the navigation context and associate it with
// a SuspensionManager key
rootFrame = ref new Frame();
// ...
}
}
dynamic_cast
的另一個用途是探查 Object^
來判斷它是否包含 Boxed 實值類型。 在這種情況下,您會嘗試 dynamic_cast<Platform::Box>
或 dynamic_cast<Platform::IBox>
。
dynamic_cast 和追蹤參考 (%)
您也可以將 套用 dynamic_cast
至追蹤參考,但在此情況下,轉換的行為就像 safe_cast。 它會在失敗時擲回 Platform::InvalidCastException
,因為追蹤參考不能有 nullptr
值。
reinterpret_cast
我們建議您不要使用 reinterpret_cast ,因為編譯時期檢查和執行階段檢查都不會執行。 在最壞的情況下, reinterpret_cast
可讓程式設計錯誤在開發階段未偵測到,並在程序的行為中造成細微或災難性的錯誤。 因此,我們建議您只有在必須於不相關的類型之間轉型,而且您知道轉型將會成功的罕見情況下,才能使用 reinterpret_cast
。 罕見的用法範例是將 Windows 執行階段 類型轉換成其基礎 ABI 類型,這表示您要控制對象的參考計數。 若要這麼做,我們建議您使用 ComPtr Class 智慧型指標。 否則,您必須在介面上特別呼叫 Release。 下列範例示範如何將 ref 類別轉型為 IInspectable*
。
#include <wrl.h>
using namespace Microsoft::WRL;
auto winRtObject = ref new SomeWinRTType();
ComPtr<IInspectable> inspectable = reinterpret_cast<IInspectable*>(winRtObject);
// ...
如果您使用 reinterpret_cast
從一個 Windows 執行階段 介面轉換成另一個介面,則會導致對象釋放兩次。 因此,只有在轉換成非 C++ 元件擴充介面時,才使用此轉換。
ABI 類型
ABI 類型存在於 Windows SDK 的標頭中。 為了方便起見,標頭會以命名空間命名,例如
windows.storage.h
。ABI 類型存在於特殊命名空間 ABI 中,例如
ABI::Windows::Storage::Streams::IBuffer*
。Windows 執行階段 介面類型與其對等 ABI 類型之間的轉換一律是安全的,也就是 。
IBuffer^
ABI::IBuffer*
如果已知,Windows 執行階段 類別應該一律轉換成
IInspectable*
或其預設介面。在轉換成 ABI 類型之後,您會擁有此類型的存留期,而且必須遵循 COM 規則。 我們建議您使用
WRL::ComPtr
來簡化 ABI 指標的存留期管理。
下表摘要說明可安全使用 reinterpret_cast
的案例。 在每個案例中,雙向轉型是安全的。
從 轉換,轉型為 | 轉換至,轉換自 |
---|---|
HSTRING |
String^ |
HSTRING* |
String^* |
IInspectable* |
Object^ |
IInspectable** |
Object^* |
IInspectable-derived-type* |
same-interface-from-winmd^ |
IInspectable-derived-type** |
same-interface-from-winmd^* |
IDefault-interface-of-RuntimeClass* |
same-RefClass-from-winmd^ |
IDefault-interface-of-RuntimeClass** |
same-RefClass-from-winmd^* |