C#에서 C++/WinRT로 이동Move to C++/WinRT from C#

이 항목에서는 C# 프로젝트의 소스 코드를 C++/WinRT의 해당 소스 코드로 이식하는 데 관련된 기술 세부 정보를 포괄적으로 분류합니다.This topic comprehensively catalogs the technical details involved in porting the source code in a C# project to its equivalent in C++/WinRT.

UWP(유니버설 Windows 플랫폼) 앱 샘플 중 하나를 이식하는 방법에 대한 사례 연구는 C#에서 C++/WinRT로 클립보드 샘플 이식 관련 문서를 참조하세요.For a case study of porting one of the Universal Windows Platform (UWP) app samples, see the companion topic Porting the Clipboard sample to C++/WinRT from C#. 이 연습을 통해 진행하면서 샘플을 직접 이식하여 이식 실습과 경험을 쌓을 수 있습니다.You can gain porting practice and experience by following along with that walkthrough, and porting the sample for yourself as you go.

준비 방법 및 필요한 항목How to prepare, and what to expect

C#에서 C++/WinRT로 클립보드 샘플 이식 사례 연구에서는 프로젝트를 C++/WinRT로 이식하는 동안 수행하는 다양한 소프트웨어 설계 결정의 예제를 보여 줍니다.The case study Porting the Clipboard sample to C++/WinRT from C# illustrates examples of the kinds of software design decisions that you'll make while porting a project to C++/WinRT. 따라서 기존 코드의 작동 방식을 확실하게 이해하여 이식을 준비하는 것이 좋습니다.So, it's a good idea to prepare for porting by gaining a solid understanding of how the existing code works. 이렇게 하면 앱 기능 및 코드 구조에 대해 개략적으로 확실하게 파악할 수 있습니다. 그리고 나서 결정을 내리면 항상 올바른 방향으로 나아가게 됩니다.That way, you'll get a good overview of the app's functionality, and the code's structure, and then the decisions that you make will always take you forward, and in the right direction.

필요한 이식 변경의 종류에 따라 4가지 범주로 그룹화할 수 있습니다.In terms of what kinds of porting changes to expect, you could group them into four categories.

  • 언어 프로젝션 이식.Port the language projection. WinRT(Windows 런타임)는 다양한 프로그래밍 언어로 프로젝션됩니다.The Windows Runtime (WinRT) is projected into various programming languages. 이러한 언어 프로젝션 각각은 문제의 프로그래밍 언어에 자연스러운 느낌을 주도록 설계되었습니다.Each of those language projections is designed to feel idiomatic to the programming language in question. C#의 경우 일부 Windows 런타임 형식이 .NET 형식으로 프로젝션됩니다.For C#, some Windows Runtime types are projected as .NET types. 예를 들어 System.Collections.Generic.IReadOnlyList<T> Windows.Foundation.Collections.IVectorView<T> 로 다시 변환할 수 있습니다.So for example you'll be translating System.Collections.Generic.IReadOnlyList<T> back to Windows.Foundation.Collections.IVectorView<T>. 또한 C#에서 일부 Windows 런타임 작업은 편리한 C# 언어 기능으로 프로젝션됩니다.Also in C#, some Windows Runtime operations are projected as convenient C# language features. 예를 들어 C#에서 += 연산자 구문을 사용하여 이벤트 처리 대리자를 등록합니다.An example is that in C# you use the += operator syntax to register an event-handling delegate. 이에 따라 이러한 언어 기능을 수행되는 기본 작업(이 예에서는 이벤트 등록)으로 다시 변환할 수 있습니다.So you'll be translating language features such as that back to the fundamental operation that's being performed (event registration, in this example).
  • 언어 구문 이식.Port language syntax. 이러한 변경 중 대부분은 한 기호를 다른 기호로 바꾸는 간단한 기계적 변환입니다.Many of these changes are simple mechanical transforms, replacing one symbol for another. 예를 들어 점(.)을 이중 콜론(::)으로 변경합니다.For example, changing dot (.) to double-colon (::).
  • 언어 프로시저 이식.Port language procedure. 이러한 변경 중 일부는 단순하고 반복적인 변경일 수 있습니다(예: myObject.MyProperty에서 myObject.MyProperty()로).Some of these can be simple, repetitive changes (such as myObject.MyProperty to myObject.MyProperty()). 다른 변경은 더 심층적으로 변경해야 합니다(예: System.Text.StringBuilder 사용과 관련된 프로시저를 std::wostringstream 사용과 관련된 프로시저로 이식).Others need deeper changes (for example, porting a procedure that involves the use of System.Text.StringBuilder to one that involves the use of std::wostringstream).
  • C++/WinRT와 관련된 이식 관련 작업.Porting-related tasks that are specific to C++/WinRT. Windows 런타임의 특정 세부 정보는 C# 내부에서 암시적으로 처리됩니다.Certain details of the Windows Runtime are taken care of impliclicly by C#, behind the scenes. 이러한 세부 정보는 C++/WinRT에서 명시적으로 수행됩니다.Those details are done explicitly in C++/WinRT. 예를 들어 .idl 파일을 사용하여 런타임 클래스를 정의합니다.An example is that you use an .idl file to define your runtime classes.

이 항목의 나머지 부분은 이러한 분류에 따라 구성되어 있습니다.The rest of this topic is structured according to that taxonomy.

언어 프로젝션과 관련된 변경 내용Changes that involve the language projection

범주Category C#C# C++/WinRTC++/WinRT 참고 항목See also
형식화되지 않은 개체Untyped object object 또는 System.Objectobject, or System.Object Windows::Foundation::IInspectableWindows::Foundation::IInspectable EnableClipboardContentChangedNotifications 메서드 이식Porting the EnableClipboardContentChangedNotifications method
프로젝션 네임스페이스Projection namespaces using System; using namespace Windows::Foundation;
using System.Collections.Generic; using namespace Windows::Foundation::Collections;
컬렉션 크기Size of a collection collection.Count collection.Size() BuildClipboardFormatsOutputString 메서드 이식Porting the BuildClipboardFormatsOutputString method
일반적인 컬렉션 형식Typical collection type IList<T> 및 요소를 추가하는 Add.IList<T>, and Add to add an element. IVector<T> 및 요소를 추가하는 Append.IVector<T>, and Append to add an element. 모든 곳에서 std::vector를 사용하는 경우 push_back을 사용하여 요소를 추가합니다.If you use a std::vector anywhere, then push_back to add an element.
읽기 전용 컬렉션 형식Read-only collection type IReadOnlyList<T> IReadOnlyList<T> IVectorView<T> IVectorView<T> BuildClipboardFormatsOutputString 메서드 이식Porting the BuildClipboardFormatsOutputString method
클래스 멤버인 이벤트 처리기 대리자Event handler delegate as class member myObject.EventName += Handler; token = myObject.EventName({ get_weak(), &Class::Handler }); EnableClipboardContentChangedNotifications 메서드 이식Porting the EnableClipboardContentChangedNotifications method
해지 이벤트 처리기 대리자Revoke event handler delegate myObject.EventName -= Handler; myObject.EventName(token); EnableClipboardContentChangedNotifications 메서드 이식Porting the EnableClipboardContentChangedNotifications method
결합형 컨테이너Associative container IDictionary<K, V> IDictionary<K, V> IMap<K, V> IMap<K, V>
벡터 멤버 액세스Vector member access x = v[i];
v[i] = x;
x = v.GetAt(i);
v.SetAt(i, x);

이벤트 처리기 등록/해지Register/revoke an event handler

C++/WinRT에는 C++/WinRT의 대리자를 사용한 이벤트 처리에서 설명한 대로 이벤트 처리기 대리자를 등록/해지할 수 있는 몇 가지 구문 옵션이 있습니다.In C++/WinRT, you have several syntactic options to register/revoke an event handler delegate, as described in Handle events by using delegates in C++/WinRT. 또한 EnableClipboardContentChangedNotifications 메서드 이식도 참조하세요.Also see Porting the EnableClipboardContentChangedNotifications method.

예를 들어 이벤트 수신자(이벤트를 처리하는 개체)가 소멸되려고 하는 경우 이벤트 원본(이벤트를 발생시키는 개체)이 소멸된 개체로 호출되지 않도록 이벤트 처리기를 해지하려고 할 수 있습니다.Sometimes, for example when an event recipient (an object handling an event) is about to be destroyed, you'll want to revoke an event handler so that the event source (the object raising the event) doesn't call into a destroyed object. 등록된 대리자 철회를 참조하세요.See Revoke a registered delegate. 이러한 경우 이벤트 처리기에 대한 event_token 멤버 변수를 만듭니다.In cases like that, create an event_token member variable for your event handlers. 예제는 EnableClipboardContentChangedNotifications 메서드 이식을 참조하세요.For an example, see Porting the EnableClipboardContentChangedNotifications method.

또한 이벤트 처리기를 XAML 태그에 등록할 수도 있습니다.You can also register an event handler in XAML markup.

<Button x:Name="OpenButton" Click="OpenButton_Click" />

C#에서 OpenButton_Click 메서드는 private일 수 있으며, XAML은 여전히 OpenButton에서 발생한 ButtonBase.Click 이벤트에 연결할 수 있습니다.In C#, your OpenButton_Click method can be private, and XAML will still be able to connect it to the ButtonBase.Click event raised by OpenButton.

C++/WinRT에서 XAML 태그에 등록하려면 OpenButton_Click 메서드가 구현 형식에서 public이어야 합니다.In C++/WinRT, your OpenButton_Click method must be public in your implementation type if you want to register it in XAML markup. 이벤트 처리기를 명령형 코드에만 등록하는 경우 이벤트 처리기는 public일 필요가 없습니다.If you register an event handler only in imperative code, then the event handler doesn't need to be public.

namespace winrt::MyProject::implementation
{
    struct MyPage : MyPageT<MyPage>
    {
        void OpenButton_Click(
            winrt::Windows:Foundation::IInspectable const& sender,
            winrt::Windows::UI::Xaml::RoutedEventArgs const& args);
    }
};

또는 등록 XAML 페이지를 구현 형식의 friend로 만들고, OpenButton_Click을 private으로 만들 수 있습니다.Alternatively, you can make the registering XAML page a friend of your implementation type, and OpenButton_Click private.

namespace winrt::MyProject::implementation
{
    struct MyPage : MyPageT<MyPage>
    {
    private:
        friend MyPageT;
        void OpenButton_Click(
            winrt::Windows:Foundation::IInspectable const& sender,
            winrt::Windows::UI::Xaml::RoutedEventArgs const& args);
    }
};

한 가지 마지막 시나리오는 바인드를 태그에서 이벤트 처리기로 이식할 C# 프로젝트입니다(이 시나리오에 대한 자세한 배경은 x:Bind의 함수 참조).One final scenario is where the C# project that you're porting binds to the event handler from markup (for more background on that scenario, see Functions in x:Bind).

<Button x:Name="OpenButton" Click="{x:Bind OpenButton_Click}" />

해당 태그를 보다 단순한 Click="OpenButton_Click"으로 변경하면 됩니다.You could just change that markup to the more simple Click="OpenButton_Click". 또는 원하는 경우 해당 태그를 그대로 유지할 수 있습니다.Or, if you prefer, you can keep that markup as it is. 이를 지원하려면 IDL에서 이벤트 처리기를 선언하기만 하면 됩니다.All you have to do to support it is to declare the event handler in IDL.

void OpenButton_Click(Object sender, Windows.UI.Xaml.RoutedEventArgs e);

참고

이를 Fire and forget으로 구현하더라도 함수를 void로 선언합니다.Declare the function as void even if you implement it as Fire and forget.

언어 구문과 관련된 변경 내용Changes that involve the language syntax

범주Category C#C# C++/WinRTC++/WinRT 참고 항목See also
액세스 한정자Access modifiers public \<member\> public:
    \<member\>
Button_Click 메서드 이식Porting the Button_Click method
데이터 멤버 액세스Access a data member this.variable this->variable
비동기 작업Async action async Task ... IAsyncAction ...
비동기 연산Async operation async Task<T> ... IAsyncOperation<T> ...
fire-and-forget 메서드(비동기 함축)Fire-and-forget method (implies async) async void ... winrt::fire_and_forget ... CopyButton_Click 메서드 이식Porting the CopyButton_Click method
열거형 상수 액세스Access an enumerated constant E.Value E::Value DisplayChangedFormats 메서드 이식Porting the DisplayChangedFormats method
협조적 대기Cooperatively wait await ... co_await ... CopyButton_Click 메서드 이식Porting the CopyButton_Click method
프로젝션된 형식 컬렉션(프라이빗 필드)Collection of projected types as a private field private List<MyRuntimeClass> myRuntimeClasses = new List<MyRuntimeClass>(); std::vector
<MyNamespace::MyRuntimeClass>
m_myRuntimeClasses;
GUID 생성GUID construction private static readonly Guid myGuid = new Guid("C380465D-2271-428C-9B83-ECEA3B4A85C1"); winrt::guid myGuid{ 0xC380465D, 0x2271, 0x428C, { 0x9B, 0x83, 0xEC, 0xEA, 0x3B, 0x4A, 0x85, 0xC1} };
네임스페이스 구분 기호Namespace separator A.B.T A::B::T
NullNull null nullptr UpdateStatus 메서드 이식Porting the UpdateStatus method
형식 개체 가져오기Obtain a type object typeof(MyType) winrt::xaml_typename<MyType>() Scenarios 속성 이식Porting the Scenarios property
메서드에 대한 매개 변수 선언Parameter declaration for a method MyType MyType const& 매개 변수 전달Parameter-passing
비동기 메서드에 대한 매개 변수 선언Parameter declaration for an async method MyType MyType 매개 변수 전달Parameter-passing
정적 메서드 호출Call a static method T.Method() T::Method()
문자열Strings string 또는 System.Stringstring, or System.String winrt::hstringwinrt::hstring C++/WinRT의 문자열 처리String handling in C++/WinRT
문자열 리터럴String literal "a string literal" L"a string literal" 생성자, CurrentFEATURE_NAME 이식Porting the constructor, Current, and FEATURE_NAME
유추(또는 추론) 형식Inferred (or deduced) type var auto BuildClipboardFormatsOutputString 메서드 이식Porting the BuildClipboardFormatsOutputString method
using 지시문Using-directive using A.B.C; using namespace A::B::C; 생성자, CurrentFEATURE_NAME 이식Porting the constructor, Current, and FEATURE_NAME
축자/원시 문자열 리터럴Verbatim/raw string literal @"verbatim string literal" LR"(raw string literal)" DisplayToast 메서드 이식Porting the DisplayToast method

참고

헤더 파일에 지정된 네임스페이스에 대한 using namespace 지시문이 없으면 해당 네임스페이스에 대한 모든 형식 이름을 정규화하거나 적어도 컴파일러에서 찾을 수 있을 만큼 충분히 한정해야 합니다.If a header file doesn't contain a using namespace directive for a given namespace, then you'll have to fully-qualify all type names for that namespace; or at least qualify them sufficiently for the compiler to find them. 예제는 DisplayToast 메서드 이식을 참조하세요.For an example, see Porting the DisplayToast method.

클래스 및 멤버 이식Porting classes and members

각 C# 형식에 대해 Windows 런타임 형식 또는 일반 C++ 클래스/구조체/열거형으로 이식할지 여부를 결정해야 합니다.You'll need to decide, for each C# type, whether to port it to a Windows Runtime type, or to a regular C++ class/struct/enumeration. 이러한 결정을 내리는 방법에 대한 자세한 내용과 예제는 C#에서 C++/WinRT로 클립보드 샘플 이식을 참조하세요.For more info, and detailed examples illustrating how to make those decisions, see Porting the Clipboard sample to C++/WinRT from C#.

C# 속성은 일반적으로 접근자 함수, 변경자(mutator) 함수 및 지원 데이터 멤버가 됩니다.A C# property typically becomes an accessor function, a mutator function, and a backing data member. 자세한 내용과 예제는 IsClipboardContentChangedEnabled 속성 이식을 참조하세요.For more info, and an example, see Porting the IsClipboardContentChangedEnabled property.

비정적 필드의 경우 구현 형식의 데이터 멤버로 만듭니다.For non-static fields, make them data members of your implementation type.

C# 정적 필드는 C++/WinRT 정적 접근자 및/또는 변경자 함수가 됩니다.A C# static field becomes a C++/WinRT static accessor and/or mutator function. 자세한 내용과 예제는 생성자, CurrentFEATURE_NAME 이식을 참조하세요.For more info, and an example, see Porting the constructor, Current, and FEATURE_NAME.

멤버 함수의 경우 각 함수에 대해 IDL에 속하는지 여부 또는 구현 형식에 대한 public 또는 private 멤버 함수인지 여부를 결정해야 합니다.For member functions, again, you'll need to decide for each one whether or not it belongs in the IDL, or whether it's a public or private member function of your implementation type. 자세한 내용 및 결정하는 방법에 대한 예제는 MainPage 형식에 대한 IDL을 참조하세요.For more info, and examples of how to decide, see IDL for the MainPage type.

XAML 태그 및 자산 파일 이식Porting XAML markup, and asset files

C#에서 C++/WinRT로 클립보드 샘플 이식에서는 C# 및 C++/WinRT 프로젝트에서 동일한 XAML 태그(리소스 포함) 및 자산 파일을 사용할 수 있었습니다.In the case of Porting the Clipboard sample to C++/WinRT from C#, we were able to use the same XAML markup (including resources) and asset files across the C# and the C++/WinRT project. 이를 위해 태그를 편집해야 하는 경우도 있습니다.In some cases, edits to markup will be necessary to achieve that. MainPage 이식을 완료하는 데 필요한 XAML 및 스타일 복사를 참조하세요.See Copy the XAML and styles necessary to finish up porting MainPage.

언어 내의 프로시저와 관련된 변경 내용Changes that involve procedures within the language

범주Category C#C# C++/WinRTC++/WinRT 참고 항목See also
비동기 메서드의 수명 관리Lifetime management in an async method 해당 없음N/A auto lifetime{ get_strong() }; 또는auto lifetime{ get_strong() }; or
auto lifetime = get_strong();
CopyButton_Click 메서드 이식Porting the CopyButton_Click method
삭제Disposal using (var t = v) auto t{ v };
t.Close(); // or let wrapper destructor do the work
CopyImage 메서드 이식Porting the CopyImage method
개체 생성Construct object new MyType(args) MyType{ args } 또는MyType{ args } or
MyType(args)
Scenarios 속성 이식Porting the Scenarios property
초기화되지 않은 참조 만들기Create uninitialized reference MyType myObject; MyType myObject{ nullptr }; 또는MyType myObject{ nullptr }; or
MyType myObject = nullptr;
생성자, CurrentFEATURE_NAME 이식Porting the constructor, Current, and FEATURE_NAME
인수를 사용하여 개체를 변수로 생성Construct object into variable with args var myObject = new MyType(args); auto myObject{ MyType{ args } }; 또는auto myObject{ MyType{ args } }; or
auto myObject{ MyType(args) }; 또는auto myObject{ MyType(args) }; or
auto myObject = MyType{ args }; 또는auto myObject = MyType{ args }; or
auto myObject = MyType(args); 또는auto myObject = MyType(args); or
MyType myObject{ args }; 또는MyType myObject{ args }; or
MyType myObject(args);
Footer_Click 메서드 이식Porting the Footer_Click method
인수를 사용하지 않고 개체를 변수로 생성Construct object into variable without args var myObject = new T(); MyType myObject; BuildClipboardFormatsOutputString 메서드 이식Porting the BuildClipboardFormatsOutputString method
개체 초기화 줄임Object initialization shorthand var p = new FileOpenPicker{
    ViewMode = PickerViewMode.List
};
FileOpenPicker p;
p.ViewMode(PickerViewMode::List);
대량 벡터 작업Bulk vector operation var p = new FileOpenPicker{
    FileTypeFilter = { ".png", ".jpg", ".gif" }
};
FileOpenPicker p;
p.FileTypeFilter().ReplaceAll({ L".png", L".jpg", L".gif" });
CopyButton_Click 메서드 이식Porting the CopyButton_Click method
컬렉션 반복Iterate over collection foreach (var v in c) for (auto&& v : c) BuildClipboardFormatsOutputString 메서드 이식Porting the BuildClipboardFormatsOutputString method
예외 catchCatch an exception catch (Exception ex) catch (winrt::hresult_error const& ex) PasteButton_Click 메서드 이식Porting the PasteButton_Click method
예외 세부 정보Exception details ex.Message ex.message() PasteButton_Click 메서드 이식Porting the PasteButton_Click method
속성 값 가져오기Get a property value myObject.MyProperty myObject.MyProperty() NotifyUser 메서드 이식Porting the NotifyUser method
속성 값 설정Set a property value myObject.MyProperty = value; myObject.MyProperty(value);
속성 값 증가Increment a property value myObject.MyProperty += v; myObject.MyProperty(thing.Property() + v);
문자열의 경우 작성기로 전환For strings, switch to a builder
ToString()ToString() myObject.ToString() winrt::to_hstring(myObject) ToString()ToString()
언어 문자열을 Windows 런타임 문자열로 변환Language string to Windows Runtime string 해당 없음N/A winrt::hstring{ s }
문자열 작성String-building StringBuilder builder;
builder.Append(...);
std::wostringstream builder;
builder << ...;
문자열 작성String-building
문자열 보간String interpolation $"{i++}) {s.Title}" winrt::to_hstring 및/또는 winrt::hstring::operator+ winrt::to_hstring, and/or winrt::hstring::operator+ OnNavigatedTo 메서드 이식Porting the OnNavigatedTo method
비교할 빈 문자열Empty string for comparison System.String.EmptySystem.String.Empty winrt::hstring::emptywinrt::hstring::empty UpdateStatus 메서드 이식Porting the UpdateStatus method
빈 문자열 만들기Create empty string var myEmptyString = String.Empty; winrt::hstring myEmptyString{ L"" };
사전 작업Dictionary operations map[k] = v; // replaces any existing
v = map[k]; // throws if not present
map.ContainsKey(k)
map.Insert(k, v); // replaces any existing
v = map.Lookup(k); // throws if not present
map.HasKey(k)
형식 변환(실패 시 throw)Type conversion (throw on failure) (MyType)v v.as<MyType>() Footer_Click 메서드 이식Porting the Footer_Click method
형식 변환(실패 시 null)Type conversion (null on failure) v as MyType v.try_as<MyType>() PasteButton_Click 메서드 이식Porting the PasteButton_Click method
X:Name이 있는 XAML 요소는 속성임XAML elements with x:Name are properties MyNamedElement MyNamedElement() 생성자, CurrentFEATURE_NAME 이식Porting the constructor, Current, and FEATURE_NAME
UI 스레드로 전환Switch to the UI thread CoreDispatcher.RunAsyncCoreDispatcher.RunAsync CoreDispatcher.RunAsync 또는 winrt::resume_foregroundCoreDispatcher.RunAsync, or winrt::resume_foreground NotifyUser 메서드 이식HistoryAndRoaming 메서드 이식Porting the NotifyUser method, and Porting the HistoryAndRoaming method
XAML 페이지의 명령형 코드에서 UI 요소 생성UI element construction in imperative code in a XAML page UI 요소 생성 참조See UI element construction UI 요소 생성 참조See UI element construction

다음 섹션에서는 표의 일부 항목에 대해 자세히 설명합니다.The following sections go into more detail regarding some of the items in the table.

UI 요소 생성UI element construction

다음 코드 예제는 XAML 페이지의 명령형 코드에서 UI 요소를 생성하는 방법을 보여줍니다.These code examples show the construction of a UI element in the imperative code of a XAML page.

var myTextBlock = new TextBlock()
{
    Text = "Text",
    Style = (Windows.UI.Xaml.Style)this.Resources["MyTextBlockStyle"]
};
TextBlock myTextBlock;
myTextBlock.Text(L"Text");
myTextBlock.Style(
    winrt::unbox_value<Windows::UI::Xaml::Style>(
        Resources().Lookup(
            winrt::box_value(L"MyTextBlockStyle")
        )
    )
);

ToString()ToString()

C# 형식은 Object.ToString 메서드를 제공합니다.C# types provide the Object.ToString method.

int i = 2;
var s = i.ToString(); // s is a System.String with value "2".

C++/WinRT는 이 기능을 직접 제공하지 않지만 대체 기능으로 전환할 수 있습니다.C++/WinRT doesn't directly provide this facility, but you can turn to alternatives.

int i{ 2 };
auto s{ std::to_wstring(i) }; // s is a std::wstring with value L"2".

또한 C++/WinRT는 제한된 수의 형식에 대해 winrt::to_hstring도 지원합니다.C++/WinRT also supports winrt::to_hstring for a limited number of types. 문자열화하려는 추가 형식에 대한 오버로드를 추가해야 합니다.You'll need to add overloads for any additional types you want to stringify.

LanguageLanguage 정수 문자열화Stringify int 열거형 문자열화Stringify enum
C#C# string result = "hello, " + intValue.ToString();
string result = $"hello, {intValue}";
string result = "status: " + status.ToString();
string result = $"status: {status}";
C++/WinRTC++/WinRT hstring result = L"hello, " + to_hstring(intValue); // must define overload (see below)
hstring result = L"status: " + to_hstring(status);

열거형을 문자열화하는 경우 winrt::to_hstring의 구현을 제공해야 합니다.In the case of stringifying an enum, you will need to provide the implementation of winrt::to_hstring.

namespace winrt
{
    hstring to_hstring(StatusEnum status)
    {
        switch (status)
        {
        case StatusEnum::Success: return L"Success";
        case StatusEnum::AccessDenied: return L"AccessDenied";
        case StatusEnum::DisabledByPolicy: return L"DisabledByPolicy";
        default: return to_hstring(static_cast<int>(status));
        }
    }
}

이러한 문자열화는 데이터 바인딩에서 암시적으로 사용되는 경우가 많습니다.These stringifications are often consumed implicitly by data binding.

<TextBlock>
You have <Run Text="{Binding FlowerCount}"/> flowers.
</TextBlock>
<TextBlock>
Most recent status is <Run Text="{x:Bind LatestOperation.Status}"/>.
</TextBlock>

이러한 바인딩은 bound 속성의 winrt::to_hstring을 수행합니다.These bindings will perform winrt::to_hstring of the bound property. 두 번째 예제(StatusEnum)의 경우 winrt::to_hstring에 대한 사용자 고유의 오버로드를 제공해야 합니다. 그렇지 않으면 컴파일러 오류가 발생합니다.In the case of the second example (the StatusEnum), you must provide your own overload of winrt::to_hstring, otherwise you'll get a compiler error.

Footer_Click 메서드 이식도 참조하세요.Also see Porting the Footer_Click method.

문자열 작성String-building

문자열 작성의 경우 C#에는 기본 제공 StringBuilder 형식이 있습니다.For string building, C# has a built-in StringBuilder type.

범주Category C#C# C++/WinRTC++/WinRT
문자열 작성String-building StringBuilder builder;
builder.Append(...);
std::wostringstream builder;
builder << ...;
Windows 런타임 문자열 추가, null 유지Append a Windows Runtime string, preserving nulls builder.Append(s); builder << std::wstring_view{ s };
줄 바꿈 추가Add a newline builder.Append(Environment.NewLine); builder << std::endl;
결과 액세스Access the result s = builder.ToString(); ws = builder.str();

BuildClipboardFormatsOutputString 메서드 이식DisplayChangedFormats 메서드 이식도 참조하세요.Also see Porting the BuildClipboardFormatsOutputString method, and Porting the DisplayChangedFormats method.

기본 UI 스레드에서 코드 실행Running code on the main UI thread

이 예제는 바코드 스캐너 샘플에서 가져온 것입니다.This example is taken from the Barcode scanner sample.

C# 프로젝트의 기본 UI 스레드에서 작업하려는 경우 일반적으로 다음과 같이 CoreDispatcher.RunAsync 메서드를 사용합니다.When you want to do work on the main UI thread in a C# project, you typically use the CoreDispatcher.RunAsync method, like this.

private async void Watcher_Added(DeviceWatcher sender, DeviceInformation args)
{
    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        // Do work on the main UI thread here.
    });
}

C++/WinRT에서 표현하는 것이 훨씬 간단합니다.It's much simpler to express that in C++/WinRT. 첫 번째 일시 중단 지점(여기서는 co_await) 후에 액세스하려 한다는 전제 하에 값을 기준으로 매개 변수를 허용합니다.Notice that we're accepting parameters by value on the assumption we'll want to access them after the first suspension point (the co_await, in this case). 자세한 내용은 매개 변수 전달을 참조하세요.For more info, see Parameter-passing.

winrt::fire_and_forget Watcher_Added(DeviceWatcher sender, winrt::DeviceInformation args)
{
    co_await Dispatcher();
    // Do work on the main UI thread here.
}

기본값이 아닌 우선 순위에 따라 작업을 수행해야 하는 경우에는 우선 순위를 사용하는 오버로드가 있는 winrt::resume_foreground 함수를 참조하세요.If you need to do the work at a priority other than the default, then see the winrt::resume_foreground function, which has an overload that takes a priority. winrt::resume_foreground 호출을 대기하는 방법을 보여주는 코드 예제는 스레드 선호도를 고려한 프로그래밍을 참조하세요.For code examples showing how to await a call to winrt::resume_foreground, see Programming with thread affinity in mind.

IDL에서 런타임 클래스 정의Define your runtime classes in IDL

MainPage 형식에 대한 IDL.idl 파일 통합을 참조하세요.See IDL for the MainPage type, and Consolidate your .idl files.

필요한 C++/WinRT Windows 네임스페이스 헤더 파일 포함Include the C++/WinRT Windows namespace header files that you need

C++/WinRT에서 Windows 네임스페이스의 형식을 사용하려면 해당 C++/WinRT Windows 네임스페이스 헤더 파일을 포함해야 합니다.In C++/WinRT, whenever you want to use a type from a Windows namespaces, you need to include the corresponding C++/WinRT Windows namespace header file. 예제는 NotifyUser 메서드 이식을 참조하세요.For an example, see Porting the NotifyUser method.

boxing 및 unboxingBoxing and unboxing

C#은 자동으로 스칼라를 개체에 boxing합니다.C# automatically boxes scalars into objects. C++/WinRT에서는 winrt::box_value 함수를 명시적으로 호출해야 합니다.C++/WinRT requires you to call the winrt::box_value function explicitly. 두 언어에서 모두 명시적으로 unboxing해야 합니다.Both languages require you to unbox explicitly. C++/WinRT를 사용하여 boxing 및 unboxing을 참조하세요.See Boxing and unboxing with C++/WinRT.

다음 표에서는 이러한 정의를 사용합니다.In the tables that follows, we'll use these definitions.

C#C# C++/WinRTC++/WinRT
int i; int i;
string s; winrt::hstring s;
object o; IInspectable o;
작업Operation C#C# C++/WinRTC++/WinRT
boxingBoxing o = 1;
o = "string";
o = box_value(1);
o = box_value(L"string");
unboxingUnboxing i = (int)o;
s = (string)o;
i = unbox_value<int>(o);
s = unbox_value<winrt::hstring>(o);

C++/CX 및 C#에서는 값 형식에 대한 null 포인터를 unboxing하려고 하면 예외가 발생합니다.C++/CX and C# raise exceptions if you try to unbox a null pointer to a value type. C++/WinRT는 이를 프로그래밍 오류로 간주하여 작동이 중단됩니다.C++/WinRT considers this a programming error, and it crashes. C++/WinRT에서 개체가 예상한 형식이 아닌 경우를 처리하려면 winrt::unbox_value_or 함수를 사용합니다.In C++/WinRT, use the winrt::unbox_value_or function if you want to handle the case where the object is not of the type that you thought it was.

시나리오Scenario C#C# C++/WinRTC++/WinRT
알려진 정수 unboxingUnbox a known integer i = (int)o; i = unbox_value<int>(o);
o가 null인 경우If o is null System.NullReferenceException 작동 중단Crash
o가 boxing된 정수가 아닌 경우If o is not a boxed int System.InvalidCastException 작동 중단Crash
정수 unboxing, null인 경우 대체 사용, 다른 항목이 있는 경우 작동 중단Unbox int, use fallback if null; crash if anything else i = o != null ? (int)o : fallback; i = o ? unbox_value<int>(o) : fallback;
가능한 경우 정수 unboxing, 다른 항목이 있는 경우 대체 사용Unbox int if possible; use fallback for anything else i = as int? ?? fallback; i = unbox_value_or<int>(o, fallback);

예제는 OnNavigatedTo 메서드 이식Footer_Click 메서드 이식을 참조하세요.For an example, see Porting the OnNavigatedTo method, and Porting the Footer_Click method.

문자열 boxing 및 unboxingBoxing and unboxing a string

문자열은 어떤 방식에서는 값 형식이고, 다른 방식에서는 참조 형식입니다.A string is in some ways a value type, and in other ways a reference type. C# 및 C++/WinRT는 문자열을 다르게 처리합니다.C# and C++/WinRT treat strings differently.

HSTRING ABI 형식은 참조 횟수가 계산되는 문자열에 대한 포인터입니다.The ABI type HSTRING is a pointer to a reference-counted string. 그러나 IInspectable에서 파생되지 않으므로 기술적으로는 개체가 아닙니다.But it doesn't derive from IInspectable, so it's not technically an object. 또한 null HSTRING은 빈 문자열을 나타냅니다.Furthermore, a null HSTRING represents the empty string. IInspectable에서 파생되지 않은 것에 대한 boxing은 IReference<T> 안에 래핑하여 수행되며, Windows 런타임에서 표준 구현을 PropertyValue 개체 형식으로 제공합니다(사용자 지정 형식은 PropertyType::OtherType으로 보고됨).Boxing of things not derived from IInspectable is done by wrapping them inside an IReference<T>, and the Windows Runtime provides a standard implementation in the form of the PropertyValue object (custom types are reported as PropertyType::OtherType).

C#은 Windows 런타임 문자열을 참조 형식으로 나타내는 반면, C++/WinRT는 문자열을 값 형식으로 프로젝션합니다.C# represents a Windows Runtime string as a reference type; while C++/WinRT projects a string as a value type. 즉, boxing된 null 문자열은 해당 문자열을 가져온 방식에 따라 다르게 표현될 수 있습니다.This means that a boxed null string can have different representations depending how you got there.

동작Behavior C#C# C++/WinRTC++/WinRT
선언Declarations object o;
string s;
IInspectable o;
hstring s;
문자열 형식 범주String type category 참조 형식Reference type 값 유형Value type
null HSTRING에서 프로젝션하는 형식null HSTRING projects as "" hstring{}
null 및 ""가 동일한가요?Are null and "" identical? 아니요No Yes
null의 유효성Validity of null s = null;
s.Length에서 NullReferenceException 발생s.Length raises NullReferenceException
s = hstring{};
s.size() == 0(유효)s.size() == 0 (valid)
개체에 null 문자열을 할당하는 경우If you assign null string to object o = (string)null;
o == null
o = box_value(hstring{});
o != nullptr
개체에 ""을(를) 할당하는 경우If you assign "" to object o = "";
o != null
o = box_value(hstring{L""});
o != nullptr

기본 boxing 및 unboxing.Basic boxing and unboxing.

작업Operation C#C# C++/WinRTC++/WinRT
문자열 boxingBox a string o = s;
빈 문자열은 null이 아닌 개체가 됩니다.Empty string becomes non-null object.
o = box_value(s);
빈 문자열은 null이 아닌 개체가 됩니다.Empty string becomes non-null object.
알려진 문자열 unboxingUnbox a known string s = (string)o;
Null 개체는 null 문자열이 됩니다.Null object becomes null string.
문자열이 아닌 경우 InvalidCastException이 발생합니다.InvalidCastException if not a string.
s = unbox_value<hstring>(o);
Null 개체가 충돌합니다.Null object crashes.
문자열이 아닌 경우 충돌이 발생합니다.Crash if not a string.
가능한 문자열 UnboxUnbox a possible string s = o as string;
Null 개체 또는 비문자열은 null 문자열이 됩니다.Null object or non-string becomes null string.

또는OR

s = o as string ?? fallback;
Null 또는 비문자열이 대체됩니다.Null or non-string becomes fallback.
빈 문자열이 유지됩니다.Empty string preserved.
s = unbox_value_or<hstring>(o, fallback);
Null 또는 비문자열이 대체됩니다.Null or non-string becomes fallback.
빈 문자열이 유지됩니다.Empty string preserved.

{Binding} 태그 확장에서 사용할 수 있는 클래스 만들기Making a class available to the {Binding} markup extension

{Binding} 태그 확장을 사용하여 데이터를 데이터 형식에 바인딩하려면 {Binding}을 사용하여 선언된 Binding 개체를 참조하세요.If you intend to use the {Binding} markup extension to data bind to your data type, then see Binding object declared using {Binding}.

XAML 태그에서 개체 사용Consuming objects from XAML markup

C# 프로젝트에서는 XAML 태그에서 프라이빗 멤버 및 명명된 요소를 사용할 수 있습니다.In a C# project, you can consume private members and named elements from XAML markup. 그러나 C++/WinRT에서 XAML {x:Bind} 태그 확장을 통해 사용되는 모든 엔터티는 IDL에 공개적으로 노출되어야 합니다.But in C++/WinRT, all entities consumed by using the XAML {x:Bind} markup extension must be exposed publicly in IDL.

또한 부울에 바인딩하면 C#에서 true 또는 false가 표시되지만, C++/WinRT에서는 Windows.Foundation.IReference`1<Boolean> 이 표시됩니다.Also, binding to a Boolean displays true or false in C#, but it shows Windows.Foundation.IReference`1<Boolean> in C++/WinRT.

자세한 내용과 코드 예제는 태그에서 개체 사용을 참조하세요.For more info, and code examples, see Consuming objects from markup.

XAML 태그에서 데이터 원본을 사용할 수 있도록 만들기Making a data source available to XAML markup

C++/WinRT 버전 2.0.190530.8 이상에서 winrt::single_threaded_observable_vectorIObservableVector<T>IObservableVector<IInspectable> 를 모두 지원하는 관찰 가능한 벡터를 만듭니다.In C++/WinRT version 2.0.190530.8 and higher, winrt::single_threaded_observable_vector creates an observable vector that supports both IObservableVector<T> and IObservableVector<IInspectable>. 예제는 Scenarios 속성 이식을 참조하세요.For an example, see Porting the Scenarios property.

다음과 같이 Midl 파일(.idl) 을 작성할 수 있습니다(런타임 클래스를 Midl 파일(.idl)로 팩터링도 참조).You can author your Midl file (.idl) like this (also see Factoring runtime classes into Midl files (.idl)).

namespace Bookstore
{
    runtimeclass BookSku { ... }

    runtimeclass BookstoreViewModel
    {
        Windows.Foundation.Collections.IObservableVector<BookSku> BookSkus{ get; };
    }

    runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
    {
        MainPage();
        BookstoreViewModel MainViewModel{ get; };
    }
}

그리고 다음과 같이 구현합니다.And implement like this.

// BookstoreViewModel.h
...
struct BookstoreViewModel : BookstoreViewModelT<BookstoreViewModel>
{
    BookstoreViewModel()
    {
        m_bookSkus = winrt::single_threaded_observable_vector<Bookstore::BookSku>();
        m_bookSkus.Append(winrt::make<Bookstore::implementation::BookSku>(L"To Kill A Mockingbird"));
    }
    
    Windows::Foundation::Collections::IObservableVector<Bookstore::BookSku> BookSkus();
    {
        return m_bookSkus;
    }

private:
    Windows::Foundation::Collections::IObservableVector<Bookstore::BookSku> m_bookSkus;
};
...

자세한 내용은 XAML 항목 컨트롤, C++/WinRT 컬렉션에 바인딩C++/WinRT로 작성된 컬렉션을 참조하세요.For more info, see XAML items controls; bind to a C++/WinRT collection, and Collections with C++/WinRT.

XAML 태그에서 데이터 원본을 사용할 수 있도록 만들기(C++/WinRT 2.0.190530.8 이전)Making a data source available to XAML markup (prior to C++/WinRT 2.0.190530.8)

XAML 데이터 바인딩을 수행하려면 항목 원본에서 IIterable<IInspectable> 뿐만 아니라 다음 인터페이스 조합 중 하나도 구현해야 합니다.XAML data binding requires that an items source implements IIterable<IInspectable>, as well as one of the following combinations of interfaces.

  • IObservableVector<IInspectable>IObservableVector<IInspectable>
  • IBindableVectorINotifyCollectionChangedIBindableVector and INotifyCollectionChanged
  • IBindableVectorIBindableObservableVectorIBindableVector and IBindableObservableVector
  • IBindableVector 자체(변경에 응답하지 않음)IBindableVector by itself (will not respond to changes)
  • IVector<IInspectable>IVector<IInspectable>
  • IBindableIterable(요소를 반복하여 프라이빗 컬렉션에 저장)IBindableIterable (will iterate and save elements into a private collection)

IVector<T> 와 같은 제네릭 인터페이스는 런타임에 검색할 수 없습니다.A generic interface such as IVector<T> can't be detected at runtime. IVector<T> 에는 T의 함수인 다른 IID(인터페이스 식별자)가 있습니다. 모든 개발자는 임의로 T 집합을 확장할 수 있으므로 XAML 바인딩 코드는 쿼리할 전체 집합을 알 수 없습니다.Each IVector<T> has a different interface identifier (IID), which is a function of T. Any developer can expand the set of T arbitrarily, so clearly the XAML binding code can never know the full set to query for. IEnumerable<T> 를 구현하는 모든 CLR 개체에서 IEnumerable을 자동으로 구현하므로 이 제한은 C#에서 문제가 되지 않습니다.That restriction isn't a problem for C# because every CLR object that implements IEnumerable<T> automatically implements IEnumerable. ABI 수준에서는 IObservableVector<T> 를 구현하는 모든 개체에서 IObservableVector<IInspectable> 를 자동으로 구현한다는 것을 의미합니다.At the ABI level, that means that every object that implements IObservableVector<T> automatically implements IObservableVector<IInspectable>.

C++/WinRT는 이러한 보장을 제공하지 않습니다.C++/WinRT doesn't offer that guarantee. C++/WinRT 런타임 클래스에서 IObservableVector<T> 를 구현하는 경우 IObservableVector<IInspectable> 의 구현도 어떻게든 제공된다고 가정할 수 없습니다.If a C++/WinRT runtime class implements IObservableVector<T>, then we can't assume that an implementation of IObservableVector<IInspectable> is somehow also provided.

따라서 이전 예제를 조사해야 하는 방법은 다음과 같습니다.Consequently, here's how the previous example will need to look.

...
runtimeclass BookstoreViewModel
{
    // This is really an observable vector of BookSku.
    Windows.Foundation.Collections.IObservableVector<Object> BookSkus{ get; };
}

그리고 구현은 다음과 같습니다.And the implementation.

// BookstoreViewModel.h
...
struct BookstoreViewModel : BookstoreViewModelT<BookstoreViewModel>
{
    BookstoreViewModel()
    {
        m_bookSkus = winrt::single_threaded_observable_vector<Windows::Foundation::IInspectable>();
        m_bookSkus.Append(winrt::make<Bookstore::implementation::BookSku>(L"To Kill A Mockingbird"));
    }
    
    // This is really an observable vector of BookSku.
    Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> BookSkus();
    {
        return m_bookSkus;
    }

private:
    Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> m_bookSkus;
};
...

m_bookSkus의 개체에 액세스해야 하는 경우 해당 개체를 Bookstore::BookSku로 다시 QI해야 합니다.If you need to access objects in m_bookSkus, then you'll need to QI them back to Bookstore::BookSku.

Widget MyPage::BookstoreViewModel(winrt::hstring title)
{
    for (auto&& obj : m_bookSkus)
    {
        auto bookSku = obj.as<Bookstore::BookSku>();
        if (bookSku.Title() == title) return bookSku;
    }
    return nullptr;
}

파생 클래스Derived classes

런타임 클래스에서 파생하려면 기본 클래스를 구성할 수 있어야 합니다.In order to derive from a runtime class, the base class must be composable. C#에서는 클래스를 구성할 수 있게 하는 특별한 단계를 수행할 필요가 없지만, C++/WinRT는 이를 수행합니다.C# doesn't require that you take any special steps to make your classes composable, but C++/WinRT does. unsealed 키워드를 사용하여 클래스를 기본 클래스로 사용할 수 있도록 지정합니다.You use the unsealed keyword to indicate that you want your class to be usable as a base class.

unsealed runtimeclass BasePage : Windows.UI.Xaml.Controls.Page
{
    ...
}
runtimeclass DerivedPage : BasePage
{
    ...
}

구현 형식에 대한 헤더 파일에서 먼저 기본 클래스 헤더 파일을 포함한 후에 파생 클래스에 대해 자동 생성된 헤더를 포함해야 합니다.In the header file for your implementation type, you must include the base class header file before you include the autogenerated header for the derived class. 그렇지 않으면 "이 형식을 식으로 잘못 사용했습니다"와 같은 오류가 발생합니다.Otherwise you'll get errors such as "Illegal use of this type as an expression".

// DerivedPage.h
#include "BasePage.h"       // This comes first.
#include "DerivedPage.g.h"  // Otherwise this header file will produce an error.

namespace winrt::MyNamespace::implementation
{
    struct DerivedPage : DerivedPageT<DerivedPage>
    {
        ...
    }
}

중요 APIImportant APIs