Что такое языковые проекции WinRT?

Что такое языковые проекции WinRT и для чего они используются?


person Alfred Myers    schedule 15.09.2011    source источник


Ответы (4)


Проекции среды выполнения Windows — это способ представления API-интерфейсов среды выполнения Windows на каждом языке. Это может происходить во время компиляции (как в C++), во время выполнения (как в JavaScript) или в комбинации (как в C#). Каждый язык решает, как лучше представить WinRT API. В большинстве случаев это прямое воздействие, но в других случаях могут иметь место обертки или перенаправления. Делегаты и события являются хорошим примером. В C# они отображаются как делегаты/события C#, а не как специфичные для WinRT типы. Строки также переназначаются, чтобы быть типом строки на родном языке, а не базовым типом hstring.

person Steve Rowe    schedule 16.09.2011

«Проекции» в WinRT — это другое слово для «привязки».

Языковые проекции WinRT — это привязки WinRT для каждого поддерживаемого языка.

Для получения дополнительной информации, проверьте:

Демистификация WinRT — Мигель де Икаса

person Justin Niessner    schedule 15.09.2011
comment
Одно тонкое отличие состоит в том, что привязки могут выполняться вручную, тогда как проекции всегда автоматизированы, т. е. вы не сидите и вручную пишете объявления FFI для WinRT с проекцией, вы просто используете один раз написанный код, который берет любой компонент WinRT и предоставляет его для рассматриваемый язык, используя идиомы этого языка. - person Pavel Minaev; 16.09.2011

Самый простой способ пояснить, что языковая проекция в WinRT — это «внешняя часть», тогда как среда выполнения Windows — это внутренняя часть. Пишите на одном из трех языков (JS, C#, VB), он ведет себя одинаково на бэкенде.

Если вы пишете свой собственный сторонний компонент WinRT на C++ или C#, вы можете использовать его из JS, C# и VB без дополнительной работы.

person Zac Brown MSFT    schedule 15.09.2011

Языковая проекция — это способ представить вам 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 Holidays
  • for 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# никто фактически не пишет проецируемые версии; компилятор и среда выполнения выполняют всю работу за кулисами, поскольку они знают, как читать метаданные.

Для других языков переведенные файлы кода создаются либо вручную, либо автоматически с помощью инструмента импорта.

person Ian Boyd    schedule 22.02.2019