Создание компонентов среды выполнения Windows в C# и Visual Basic

.NET Framework 4.5 позволяет с помощью управляемого кода создавать собственные типы Среда выполнения Windows, упакованные в компонент Среда выполнения Windows. Этот компонент можно использовать в приложениях Магазина Windows, написанных на C++, JavaScript, Visual Basic или C#. В данной статье описываются правила создания компонента и рассматриваются некоторые аспекты поддержки Среда выполнения Windows в .NET Framework. Как правило, такая поддержка разрабатывается таким образом, чтобы быть прозрачной для разработчиков .NET Framework. Однако при создании компонента, использующего JavaScript или C++, следует учитывать различия в том, как эти языки поддерживают Среда выполнения Windows.

Примечание

Если создается компонент для использования только в приложениях Магазина Windows с Visual Basic или C#, и компонент не содержит элементов управления Магазина Windows, следует использовать шаблон Библиотека классов (приложения для Магазина Windows) вместо шаблона Компонент Среда выполнения Windows. У простой библиотеки классов меньше ограничений.

Данная статья включает следующие разделы.

  • Объявление типов в компонентах среды выполнения Windows

  • Отладка компонента

  • Передача типов среды выполнения Windows в управляемый код

  • Передача управляемых типов в среду выполнения Windows

  • Передача массивов

  • Перегруженные методы

  • Асинхронные операции

  • Создание исключений

  • Объявление и вызов событий

Объявление типов в компонентах Среда выполнения Windows

Внутри компонента типы Среда выполнения Windows могут использовать любые функции .NET Framework, которые разрешены в приложении Магазина Windows. (Дополнительные сведения см. в разделе Обзор приложений .NET для Магазина Windows.) Снаружи члены ваших типов могут предоставлять доступ только к параметрам и возвращаемым значениями типов Среда выполнения Windows. В следующем списке описаны ограничения, действующие для типов .NET Framework, которые доступны в компонентах Среда выполнения Windows.

  • Поля, параметры и возвращаемые значения всех открытых типов и членов в ваших компонентах должны относиться к одному из следующих типов Среда выполнения Windows.

    Это ограничение включает типы Среда выполнения Windows, которые вы создаете, а также типы, предоставляемые Среда выполнения Windows. Кроме того, оно включает некоторые типы .NET Framework. Поддержка этих типов .NET Framework позволяет более естественным образом использовать Среда выполнения Windows в управляемом коде: код использует привычные типы .NET Framework вместо соответствующих типов Среда выполнения Windows. Например, можно использовать простые типы .NET Framework, такие как Int32 и Double, некоторые фундаментальные типы, такие как DateTimeOffset и Uri, а также некоторые часто используемые универсальные типы интерфейсов, такие как IEnumerable<T> (IEnumerable(Of T) в Visual Basic) и IDictionary<TKey,TValue>. (Обратите внимание, что аргументами типа в этих универсальных типах должны быть типы Среда выполнения Windows.) Это описано в разделах Передача типов среды выполнения Windows в управляемый код и Передача управляемых типов в среду выполнения Windows.

  • Открытые классы и интерфейсы могут содержать методы, свойства и события. Можно объявить делегаты для событий либо использовать делегат EventHandler<T>. Открытые классы и интерфейсы не могут:

    • быть универсальными;

    • реализовывать интерфейсы, отличные от интерфейсов Среда выполнения Windows. (Однако вы можете создать собственные интерфейсы Среда выполнения Windows и реализовать их.)

    • наследовать классам, отсутствующим в Среда выполнения Windows, например классам System.Exception и System.EventArgs.

  • Корневое пространство имен всех открытых типов должно совпадать с именем сборки, а имя сборки не должно начинаться на "Windows".

    Примечание

    По умолчанию имена пространств имен в проектах Visual Studio совпадают с именами сборок. В Visual Basic оператор Namespace для данного пространства имен по умолчанию в коде не отображается.

  • Открытые структуры не могут иметь членов, отличных от открытых полей, а эти поля должны иметь тип значения или строковый тип.

  • Открытые классы должны объявляться с модификатором sealed (NotInheritable в Visual Basic). Если модель программирования требует полиморфизма, можно создать открытый интерфейс и реализовать его в классах, которые должны быть полиморфными.

Отладка компонента

Если приложение Магазина Windows и компонент построены с использованием управляемого кода, их можно отлаживать одновременно.

При тестировании компонента как части приложения Магазина Windows с использованием C++ можно одновременно отлаживать управляемый и машинный код. По умолчанию может отлаживаться только машинный код.

Одновременная отладка машинного кода C++ и управляемого кода

  1. Откройте контекстное меню проекта Visual C++ и выберите пункт Свойства.

  2. На страницах свойств в разделе Свойства конфигурации выберите Отладка.

  3. Выберите Тип отладчика и в раскрывающемся списке замените значение Только машинный код на Смешанный (управляемый и машинный). Нажмите кнопку ОК.

  4. Установите точки останова в машинном и управляемом коде.

При тестировании компонента как части приложения Магазина Windows с использованием JavaScript решение по умолчанию находится в режиме отладки JavaScript. В Visual Studio 2012 и Visual Studio Express 2012 для Windows 8 можно одновременно отлаживать код JavaScript и управляемый код.

Отладка управляемого кода вместо кода JavaScript

  1. Откройте контекстное меню проекта JavaScript и выберите пункт Свойства.

  2. На страницах свойств в разделе Свойства конфигурации выберите Отладка.

  3. Выберите Тип отладчика и в раскрывающемся списке замените значение Только скрипт на Только управляемый код. Нажмите кнопку ОК.

  4. Установите точки останова в машинном коде и выполните отладку обычным образом.

Передача типов Среда выполнения Windows в управляемый код

Как было сказано в разделе Объявление типов в компонентах среды выполнения Windows, некоторые типы .NET Framework могут встречаться в сигнатурах членов открытых классов. Это является частью возможностей .NET Framework, обеспечивающих естественное использование Среда выполнения Windows в управляемом коде. Эти возможности включают простые типы, а также некоторые классы и интерфейсы. Когда компонент используется из JavaScript или из кода C++, важно знать, как типы .NET Framework представляются в вызывающем объекте. Примеры с JavaScript см. в разделе Пошаговое руководство. Создание простого компонента в C# или Visual Basic и его вызов из кода JavaScript. В этом разделе рассматриваются часто используемые типы.

В .NET Framework у простых типов, таких как структура Int32, имеется множество полезных свойств и методов, например метод TryParse. В Среда выполнения Windows, напротив, простые типы имеют только поля. При передаче этих типов в управляемый код они представляются типами .NET Framework, и свойства и методы таких типов .NET Framework можно использовать обычным образом. В следующем списке перечислены подстановки, которые автоматически выполняются в интегрированной среде разработки:

  • вместо простых типов Среда выполнения Windows Int32, Int64, Single, Double, Boolean, String (неизменяемая коллекция символов Юникода), Enum, UInt32, UInt64 и Guid используются одноименные типы из пространства имен System;

  • вместо UInt8 используется тип System.Byte;

  • вместо Char16 используется тип System.Char;

  • вместо интерфейса IInspectable используется тип System.Object.

Если в языке C# или Visual Basic имеется ключевое слово для какого-либо из этих типов, можно использовать это ключевое слово языка.

Помимо простых типов в управляемом коде в форме эквивалентов из .NET Framework используются некоторые распространенные типы Среда выполнения Windows. Предположим, например, что в коде JavaScript используется класс Windows.Foundation.Uri, и соответствующий объект необходимо передать методу C# или Visual Basic. Эквивалентным типом в управляемом коде будет класс .NET Framework System.Uri, и именно он будет типом параметра метода. Можно легко понять, в каких случаях тип Среда выполнения Windows представляется типом .NET Framework, поскольку при написании управляемого кода система IntelliSense в Visual Studio скрывает тип Среда выполнения Windows и представляет эквивалентный тип .NET Framework. (Обычно имена таких двух типов совпадают. Но следует иметь в виду, что структура Windows.Foundation.DateTime отображается в управляемом коде как System.DateTimeOffset, а не как System.DateTime.)

Для некоторых часто используемых типов коллекций сопоставление устанавливается между интерфейсами, которые реализуются типом Среда выполнения Windows, и интерфейсами, которые реализуются соответствующим типом .NET Framework. Как и в случае вышеупомянутых типов, параметры объявляются с использованием типов .NET Framework. Это позволяет скрывать некоторые различия между типами и делает написание кода .NET Framework более естественным. В следующей таблице перечислены наиболее распространенные из этих типов универсальных интерфейсов, а также другие сопоставления распространенных классов и интерфейсов. Полный список типов Среда выполнения Windows, имеющих соответствие в .NET Framework, см. в разделе Сопоставление типов .NET Framework с типами среды выполнения Windows.

Среда выполнения Windows

.NET Framework

IIterable<T>

IEnumerable<T>

IVector<T>

IList<T>

IVectorView<T>

IReadOnlyList<T>

IMap<K, V>

IDictionary<TKey, TValue>

IMapView<K, V>

IReadOnlyDictionary<TKey, TValue>

IKeyValuePair<K, V>

KeyValuePair<TKey, TValue>

IBindableIterable

IEnumerable

IBindableVector

IList

Windows.UI.Xaml.Data.INotifyPropertyChanged

System.ComponentModel.INotifyPropertyChanged

Windows.UI.Xaml.Data.PropertyChangedEventHandler

System.ComponentModel.PropertyChangedEventHandler

Windows.UI.Xaml.Data.PropertyChangedEventArgs

System.ComponentModel.PropertyChangedEventArgs

    

Если тип реализует несколько интерфейсов, в качестве типа параметра или типа возвращаемого значения члена можно использовать любой из этих интерфейсов. Например, можно передавать или возвращать тип Dictionary<int, string> (Dictionary(Of Integer, String) в Visual Basic) в качестве IDictionary<int, string>, IReadOnlyDictionary<int, string> или IEnumerable<System.Collections.Generic.KeyValuePair<TKey, TValue>>.

Важно!

В коде JavaScript используется интерфейс, занимающий первую позицию в списке интерфейсов, реализуемых управляемым типом. Например, если в код JavaScript возвращается тип Dictionary<int, string>, он отображается как IDictionary<int, string> независимо от того, какой интерфейс указан в качестве типа возвращаемого значения. Это означает, что если первый интерфейс не включает член, который отображается в последующих интерфейсах, этот член не будет видимым в JavaScript.

В Среда выполнения Windows перебор IMap<K, V> и IMapView<K, V> осуществляется с помощью IKeyValuePair. При передаче этих типов в управляемый код они представляются как IDictionary<TKey, TValue> и IReadOnlyDictionary<TKey, TValue>, поэтому для их перебора можно обычным образом использовать System.Collections.Generic.KeyValuePair<TKey, TValue>.

Представление интерфейсов в управляемом коде влияет на представление типов, реализующих эти интерфейсы. Например, класс PropertySet реализует интерфейс IMap<K, V>, который представляется в управляемом коде в виде IDictionary<TKey, TValue>. Тип PropertySet представляется, как если бы он реализовывал интерфейс IDictionary<TKey, TValue>, а не IMap<K, V>, поэтому в управляемом коде у него имеется метод Add, который работает, как метод Add словарей .NET Framework. А метода Insert у этого типа нет. Этот пример рассмотрен в статье Пошаговое руководство. Создание простого компонента в C# или Visual Basic и его вызов из кода JavaScript.

Передача управляемых типов в Среда выполнения Windows

Как было сказано в предыдущем разделе, некоторые типы Среда выполнения Windows могут представляться типами .NET Framework в сигнатурах методов компонентов или в сигнатурах членов Среда выполнения Windows при их использовании в интегрированной среде разработки. При передаче типов .NET Framework в эти члены или использовании их в качестве возвращаемых значений в членах компонентов на другой стороне они представляются как соответствующие типы Среда выполнения Windows. Примеры последствий вызова компонента из кода JavaScript см. в подразделе "Возвращение управляемых типов из компонента" раздела Пошаговое руководство. Создание простого компонента в C# или Visual Basic и его вызов из кода JavaScript.

Передача массивов

В Среда выполнения Windows все параметры являются входными или выходными; в ней нет параметров ref (ByRef в Visual Basic). Содержимое массивов, передаваемое в компонент Среда выполнения Windows, должно быть предназначено для ввода или для вывода. Иными словами, массивы должны обрабатываться как изменяемые. Если массив передается по значению (ByVal в Visual Basic), необходимо установить назначение с помощью атрибута ReadOnlyArrayAttribute или WriteOnlyArrayAttribute. См. раздел Передача массивов в компонент среды выполнения Windows.

Перегруженные методы

В Среда выполнения Windows методы можно перегружать. Однако при объявлении нескольких перегруженных методов с одинаковым количеством параметров атрибут Windows.Foundation.Metadata.DefaultOverloadAttribute должен применяться только к одной из этих перегрузок. Это будет единственная перегрузка, которую можно вызывать из JavaScript. Например, в следующем коде перегрузка, которая принимает значение int (Integer в Visual Basic) является перегрузкой по умолчанию.

        public string OverloadExample(string s)
        {
            return s;
        }
        [Windows.Foundation.Metadata.DefaultOverload()] 
        public int OverloadExample(int x)
        {
            return x;
        } 
    Public Function OverloadExample(ByVal s As String) As String
        Return s
    End Function
    <Windows.Foundation.Metadata.DefaultOverload> _
    Public Function OverloadExample(ByVal x As Integer) As Integer
        Return x
    End Function

Предупреждение

Javascript позволяет передавать в OverloadExample любое значение и приводит это значение к типу, требуемому для параметра. Можно вызвать OverloadExample со значением "forty-two", "42" или 42.3, но все они будут переданы перегруженному методу по умолчанию. В предыдущем примере перегруженный метод по умолчанию возвращает значения 0, 42 и 42 соответственно.

Атрибут DefaultOverloadAttribute нельзя применять к конструкторам. Все конструкторы в классе должны иметь различное число параметров.

Асинхронные операции

Чтобы реализовать в компоненте асинхронный метод, добавьте Async в конец имени метода и верните один из интерфейсов Среда выполнения Windows, представляющих асинхронные действия и операции: IAsyncAction, IAsyncActionWithProgress<TProgress>, IAsyncOperation<TResult> или IAsyncOperationWithProgress<TResult, TProgress>.

Для реализации асинхронных методов можно использовать задачи .NET Framework (класс Task и универсальный класс Task<TResult>). Необходимо вернуть задачу, которая представляет текущую операцию, например задачу, возвращаемую из асинхронного метода, написанного на C# или Visual Basic, или задачу, возвращаемую из метода Task.Run. При создании задачи с помощью конструктора необходимо перед возвращением задачи вызывать метод Task.Start.

Метод, который использует await (Await в Visual Basic), требует ключевого слова async (Async в Visual Basic). Если такой метод предоставляется компонентом Среда выполнения Windows, ключевое слово async следует применять к делегату, передаваемому в метод Run.

Для асинхронных действий и операций, которые не поддерживают отчеты об отмене или ходе выполнения, можно использовать метод WindowsRuntimeSystemExtensions.AsAsyncAction или метод расширения AsAsyncOperation<TResult>, чтобы заключать задачу в соответствующий интерфейс. Например, следующий код реализует асинхронный метод с помощью метода Task.Run, запускающего задачу. Метод расширения AsAsyncOperation<TResult> возвращает задачу как асинхронную операцию Среда выполнения Windows.

        public static IAsyncOperation<IList<string>> DownloadAsStringsAsync(string id)
        {
            return Task.Run<IList<string>>(async () =>
            {
                var data = await DownloadDataAsync(id);
                return ExtractStrings(data);
            }).AsAsyncOperation();
        }


    Public Shared Function DownloadAsStringsAsync(ByVal id As String) _
         As IAsyncOperation(Of IList(Of String))

        Return Task.Run(Of IList(Of String))(
            Async Function()
                Dim data = Await DownloadDataAsync(id)
                Return ExtractStrings(data)
            End Function).AsAsyncOperation()
    End Function


Следующий код JavaScript показывает, как вызывать метод с помощью объекта WinJS.Promise. Функция, которая передается методу then, выполняется при завершении асинхронного вызова. Параметр stringList содержит список строк, возвращаемый методом DownloadAsStringAsync, а функция выполняет все необходимые действия по обработке.

function asyncExample(id) {

    var result = SampleComponent.Example.downloadAsStringAsync(id).then(
        function (stringList) {
            // Place code that uses the returned list of strings here.
        });
}

Для асинхронных действий и операций, которые поддерживают отчеты об отмене и ходе выполнения, следует с помощью класса AsyncInfo создавать запущенную задачу и реализовывать функции отчетов об отмене и ходе выполнения с помощью аналогичных функций соответствующего интерфейса Среда выполнения Windows. Пример, в котором реализована поддержка отчетов об отмене и ходе выполнения, см. в разделе Пошаговое руководство. Создание простого компонента в C# или Visual Basic и его вызов из кода JavaScript.

Обратите внимание, что методы класса AsyncInfo можно использовать даже в том случае, если асинхронный метод не поддерживает отчеты об отмене и ходе выполнения. При использовании лямбда-функции Visual Basic или анонимного метода C# не указывайте параметры токена и интерфейса IProgress<T>. При использовании лямбда-функции C# указывайте параметр токена, но игнорируйте его. Предыдущий пример, в котором используется метод AsAsyncOperation<TResult>, будет выглядеть следующим образом, если вместо этого использовать перегрузку метода AsyncInfo.Run<TResult>(Func<CancellationToken, Task<TResult>>).

        public static IAsyncOperation<IList<string>> DownloadAsStringsAsync(string id)
        {
            return AsyncInfo.Run<IList<string>>(async (token) =>
            {
                var data = await DownloadDataAsync(id);
                return ExtractStrings(data);
            });
        }
    Public Function OverloadExample(ByVal s As String) As String
        Return s
    End Function
    <Windows.Foundation.Metadata.DefaultOverload> _
    Public Function OverloadExample(ByVal x As Integer) As Integer
        Return x
    End Function

При создании асинхронного метода, который может поддерживать отчеты об отмене и ходе выполнения, попробуйте добавить перегруженные версии, которые не принимают параметры токена отмены или интерфейса IProgress<T>.

Создание исключений

Можно создавать исключения любого типа, входящего в Приложения .NET для Магазина Windows — поддерживаемые API. В компоненте Среда выполнения Windows нельзя объявлять собственные открытые типы исключений, но можно объявлять и создавать неоткрытые типы.

Если компонент не обрабатывает исключение, соответствующее исключение передается в код, который вызвал данный компонент. Представление этого исключения на вызывающей стороне зависит от того, каким образом язык вызова поддерживает Среда выполнения Windows.

  • В JavaScript исключение представляется объектом, в котором сообщение исключение заменено на трассировку стека. При отладке приложения в Visual Studio исходный текст сообщения можно видеть в диалоговом окне исключений отладчика с пометкой "Сведения WinRT". Исходный текст сообщения недоступен из кода JavaScript.

    Примечание

    В настоящее время трассировка стека содержит тип управляемого исключения, однако анализировать трассировку для определения типа исключения не рекомендуется. Вместо этого лучше использовать значение HRESULT, описанное далее в этом разделе.

  • В C++ исключение является исключением платформы. Если свойство HResult управляемого исключения может быть сопоставлено значению HRESULT конкретного исключения платформы, то используется конкретное исключение; в противном случае создается исключение Platform::COMException. Текст сообщения управляемого исключения недоступен в коде C++. Если было создано конкретное исключение платформы, отображается текст сообщения по умолчанию для этого типа исключений; в противном случае текст не отображается. См. раздел Исключения (C++/CX).

  • В C# и Visual Basic используется обычное управляемое исключение.

При создании в компоненте исключения можно упростить его обработку в вызывающем коде JavaScript или C++, если создавать исключения неоткрытых типов, свойство HResult которых связано с конкретным компонентом. Значение HRESULT доступно в вызывающем коде JavaScript через свойство number объекта исключения, а в коде C++ — через свойство COMException::HResult.

Примечание

Используйте для HRESULT отрицательное значение. Положительное значение интерпретируется как успех, и исключение в вызывающем коде JavaScript или C++ не создается.

Объявление и вызов событий

При объявлении типа для хранения данных для события создавайте класс, производный от Object, а не от EventArgs, поскольку EventArgs не является типом Среда выполнения Windows. Используйте EventHandler<TEventArgs> в качестве типа события и тип аргумента своего события в качестве аргумента универсального типа. Вызов событий осуществляется так же, как и в приложениях .NET Framework.

Когда компонент Среда выполнения Windows вызывается из JavaScript или C++, событие выполняется в соответствии с шаблоном событий Среда выполнения Windows, ожидаемым этими языками. Когда компонент вызывается из C# или Visual Basic, событие представляется обычным событием .NET Framework. Соответствующий пример можно найти в разделе Пошаговое руководство. Создание простого компонента в C# или Visual Basic и его вызов из кода JavaScript.

При реализации пользовательских методов доступа к событиям (событие объявляется с ключевым словом Custom в Visual Basic) в такой реализации необходимо соблюдать шаблон событий Среда выполнения Windows. См. раздел Пользовательские события и методы доступа к событиям в компонентах среды выполнения Windows. Обратите внимание, что при обработке события в коде C# или Visual Basic оно все равно представляется обычным событием .NET Framework.

См. также

Основные понятия

Обзор приложений .NET для Магазина Windows

Приложения .NET для Магазина Windows — поддерживаемые API

Пошаговое руководство. Создание простого компонента в C# или Visual Basic и его вызов из кода JavaScript

Создание компонентов среды выполнения Windows

Пользовательские события и методы доступа к событиям в компонентах среды выполнения Windows

Передача массивов в компонент среды выполнения Windows

Сопоставление типов .NET Framework с типами среды выполнения Windows

Диагностика состояний ошибки компонентов среды выполнения Windows