Что такое языковые проекции WinRT и для чего они используются?
Что такое языковые проекции WinRT?
Ответы (4)
Проекции среды выполнения Windows — это способ представления API-интерфейсов среды выполнения Windows на каждом языке. Это может происходить во время компиляции (как в C++), во время выполнения (как в JavaScript) или в комбинации (как в C#). Каждый язык решает, как лучше представить WinRT API. В большинстве случаев это прямое воздействие, но в других случаях могут иметь место обертки или перенаправления. Делегаты и события являются хорошим примером. В C# они отображаются как делегаты/события C#, а не как специфичные для WinRT типы. Строки также переназначаются, чтобы быть типом строки на родном языке, а не базовым типом hstring.
«Проекции» в WinRT — это другое слово для «привязки».
Языковые проекции WinRT — это привязки WinRT для каждого поддерживаемого языка.
Для получения дополнительной информации, проверьте:
Демистификация WinRT — Мигель де Икаса
Самый простой способ пояснить, что языковая проекция в WinRT — это «внешняя часть», тогда как среда выполнения Windows — это внутренняя часть. Пишите на одном из трех языков (JS, C#, VB), он ведет себя одинаково на бэкенде.
Если вы пишете свой собственный сторонний компонент WinRT на C++ или C#, вы можете использовать его из JS, C# и VB без дополнительной работы.
Языковая проекция — это способ представить вам API среды выполнения Windows удобным для языка способом.
Например, основной способ создания объекта Windows.Globalization.Calendar заключается в вызове:
IInspectable instance;
HRESULT hr = RoActivateInstance(StringToHSTRING("Windows.Globalization.Calendar"), out instance);
if (Failed(hr))
throw new ComException(hr);
ICalendar calendar;
hr = instance.QueryInterface(IID_ICalendar, out calendar);
if (Failed(hr))
throw new ComException(hr);
Это то, что большинство языков называют "конструктором". Но в большинстве языков уже есть синтаксис для "создания объекта".
Если вы работаете на С#, у вас есть:
Calendar calendar = new Calendar();
Если вы используете Паскаль, у вас есть:
calendar: TCalendar;
calendar := TCalendar.Create;
Итак, давайте создадим C#-подобную оболочку (или проекцию) вокруг этого:
class Calendar : Object
{
private ICalendar _calendar;
//constructor
void Calendar() : base()
{
IInspectable instance;
HRESULT hr = RoActivateInstance(StringToHSTRING("Windows.Globalization.Calendar"), out instance);
if (Failed(hr))
throw new ComException(hr);
ICalendar calendar;
hr = instance.QueryInterface(IID_ICalendar, out calendar);
if (Failed(hr))
throw new ComException(hr);
this._calendar = calendar;
}
}
И теперь вы можете использовать свою дружественную C#-подобную проекцию:
Calendar cal = new Calendar();
Паскаль версия конструкторов
Допустим, вы используете Delphi: у вас уже есть идиома для создания объектов. Давайте преобразуем базовую сантехнику в дружественную проекцию Паскаля:
TCalendar = class
private
FCalendar: ICalendar;
public
constructor Create;
end;
constructor TCalendar.Create;
var
instance: IInspectable;
calendar: ICalendar;
hr: HRESULT;
begin
inherited Create;
hr := RoActivateInstance(StringToHSTRING('Windows.Globalization.Calendar'), {out} instance);
OleCheck(hr);
hr = instance.QueryInterface(IID_ICalendar, {out} calendar);
OleCheck(hr);
FCalendar := calendar;
end;
Теперь у нас есть проекция Delphi:
calendar: TCalendar;
calendar := TCalendar.Create;
Свойства (используйте их, если у вас есть)
В базовом интерфейсе ICalendar вы должны получать и устанавливать свойства с помощью методов:
- get_Year
- set_Year
Если вы вслепую перевели это на С#, вы могли бы получить:
Методы свойств C#:
class Calendar : Object
{
private ICalendar _calendar;
public int get_Year() { return _calendar.get_Year(); }
void set_Year(int value) { _calendar.set_Year(value); }
}
Методы свойства Pascal:
TCalendar = class
public
function get_Year: Integer;
procedure set_Year(Value: Integer);
end;
Но если ваш язык их поддерживает, вы фактически должны отображать эти свойства как фактические "Свойства". Таким образом, мы можем проецировать эти свойства, используя родной синтаксис свойств нашего языка:
С#:
class Calendar : Object
{
private ICalendar _calendar;
public int Year {
get { return _calendar.get_Year(); }
set { _calendar.set_Year(value); }
}
}
Паскаль:
TCalendar = class
public
property Year: Integer read get_Year write set_Year;
end;
ITerators
Идея состоит в том, чтобы создать фасад, который выглядит и ощущается как ваш язык, но за кулисами он сопоставляется с базовыми вызовами. Он уходит довольно глубоко.
В WinRT реализуется все перечислимое.
IIterable<T>
Но в C# все перечисляемое должно начинаться с:
IEnumerable
Таким образом, в библиотеке .NET есть внутренний класс, который адаптирует IIterable<T> и предоставляет его как IEnumerable.
Поэтому вместо метода, возвращающего IIterable<T>:
class Calendar : Object
{
public IIterable<Datetime> Holidays()
{
return _calendar.Holidays();
}
}
Он возвращает IEnumerable<T>:
class Calendar : Object
{
public IEnumerable<DateTime> Holidays()
{
IIterable<DateTime> iter = _calendar.Holidays();
//Create helper class to convert IIterable to IEnumerable
IEnumerable<DateTime> enum = new IteratorToEnumeratorAdapter(iter);
return enum;
}
}
Таким образом, вы можете использовать собственный язык:
foreach date in Holidaysfor date in Holdays do
Что дата?
В WinRT даты представлены как Windows.Foundation.DateTime:
class Calendar : Object
{
//Windows.Foundation.DateTime
Datetime Date { get { return _calendar.get_Date(); } set { _calendar.set_Date(value); }
}
Но в других языках у нас уже есть собственные классы datetime:
- С#:
System.DateTimeOffset - JavaScript:
Date - С++:
FILETIME - Дельфи:
TDateTime
Таким образом, проекция выполняет работу по преобразованию WinRT DateTime (Int64, то есть количество интервалов в 100 нс с 1 января 1601 г.) в C# DateTimeOffset:
class Calendar : Object
{
//System.DateTimeOffset
DateTimeOffset Date {
get {
Int64 ticks _calendar.get_Date().UniversalTime();
DateTimeOffset dt = DateTimeOffset.FromFileTime(ticks);
return dt;
}
set {
Int64 ticks = value.ToFileTime();
DateTime dt = new Windows.Foundation.DateTime();
dt.UniversalTime = ticks;
_calendar.set_Date(dt);
}
}
и что-то похожее на TDateTime в Delphi:
type
TCalendar = class(TObject)
private
FCalendar: ICalendar;
function getDate: TDateTime;
procedure setDate(Value: TDateTime);
public
property Date: TDateTime read getDate write setDate;
end;
function TCalendar.GetDate: TDateTime;
var
ticks: Int64;
const
OA_ZERO_TICKS = Int64(94353120000000000);
TICKS_PER_DAY = Int64(864000000000);
begin
ticks := FCalendar.get_Date().UniversalTime;
Result := (ticks - OA_ZERO_TICKS) / TICKS_PER_DAY;
end;
procedure TCalendar.SetDate(Value: TDateTime);
var
ticks: Int64;
const
OA_ZERO_TICKS = Int64(94353120000000000);
TICKS_PER_DAY = Int64(864000000000);
begin
ticks := (Value * TICKS_PER_DAY) + OA_ZERO_TICKS;
FCalendar.set_Date(Round(ticks));
end;
tl;dr
Проекция — это набор оболочек WinRT, максимально приближенный к вашему родному языку.
В C# никто фактически не пишет проецируемые версии; компилятор и среда выполнения выполняют всю работу за кулисами, поскольку они знают, как читать метаданные.
Для других языков переведенные файлы кода создаются либо вручную, либо автоматически с помощью инструмента импорта.