XAMLとコンパイルについて(Xamarin Forms)

あけましておめでとうございます。

今年も「自分の知識の整理」と「技術者としてのフェアの精神(ネットで教えてもらうだけでなく、自ら知り得た知識を共有する精神)」でブログを書いていこうと思います。

また、今年は勉強会とかにも積極的に顔を出してみようかな・・・とか思いつつ・・・

ということで、早速今回はXamarin Formsにおける小ネタを・・・

XAMLコンパイル

Xamarin Formsで開発を行う場合、UI定義はXAMLで行います。
UI定義以外のロジック(これはコードビハインドにベタ書きするものから、Prismなどのフレームワークを使ってOOに責務を分担したコードまで含めて)は C#言語 で記述します。
XAML と コードを記述し、ビルドメニューを選択するとコンパイル作業が行われ 各DLL が出力されます。このDLLはアセンブリと呼ばれる、.NETにおけるIL(Intermidiate Language)モジュールになります。この辺りは、.NETについてある程度精通している方ならご存知かと思います。C#実装は当然 IL に変換されます。
一方、XAMLはデフォルトでは「リソース」としてDLL内に埋め込まれます。そして実行時にXAML→ILへのコンパイルが行われます(補足として、実行する上では、最終的にはネイティブコードに変換)。

XamlCompilation属性

結論としては、「XamlCompilation属性」というものを用いるとXAMLをビルド時にコンパイルすることが可能になります。
以下、順を追って説明いたします。

コンパイル後のXAMLを確認する

ではまずは、デフォルトの「ビルド時にはXAMLコンパイルせず、実行時にXAMLコンパイルされる方式」について確認してみます。

①ソリューションの準備
以下のようなウィザードが生成した単純なXamarin Formsソリューションを用意します。

f:id:daigo-knowlbo:20170103115050p:plain

Xamarin FormsのPCLプロジェクトは「CompExample1」です。
CompExample1Page.xamlの定義は以下とします。Labelを2つ配置した単純なXAML定義です。

<?xml version="1.0" encoding="utf-8"?>
<ContentPage 
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
    xmlns:local="clr-namespace:CompExample1"
    x:Class="CompExample1.CompExample1Page">
  <Label Text="Welcome to Xamarin Forms!" 
       VerticalOptions="Center" 
       HorizontalOptions="Center" />
  <Label Text="test" />
</ContentPage>

②ビルド
ビルドを行います。
CompExample1/bin/Release/CompExample1.dll が出力されます。
これを ILSpy で逆コンパイルしてみましょう。
結果は以下です。
Resources配下に 対象XAMLがそのまま埋め込まれていることが確認できます。

f:id:daigo-knowlbo:20170103115528p:plain

また、フォームのコードビハインドコード内の InitializeComponent() 中で LoadFromXaml() によりXAMLリソースをロードしています。

f:id:daigo-knowlbo:20170103115548p:plain

XAMLを開発時にコンパイルする(XamlCompilation属性)

次に XamlCompilation属性 を使用してみます。
ソリューションは先程の CompExample1 を使用します。

①XamlCompilation属性の付加
XamlCompilation属性は「アセンブリ(アプリケーション)単位」もしくは「フォーム単位」で指定することができます。

アセンブリ単位の指定には、以下のように名前空間に対して「[assembly: XamlCompilation(XamlCompilationOptions.Compile)]」属性を付加します。

// アセンブリ単位に指定(App.xaml.cs)
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace CompExample1
{
  public partial class App : Application
  {
    ...
  }
  ...
}

フォーム単位の指定には、以下のようにコードビハインドクラスに対して「[XamlCompilation(XamlCompilationOptions.Compile)]」属性を付加します。

// フォーム単位に指定(CompExample1Page.xaml)
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace CompExample1
{
  [XamlCompilation(XamlCompilationOptions.Compile)]
  public partial class CompExample1Page : ContentPage
  {
    public CompExample1Page()
    {
      InitializeComponent();
    }
  }
}

②ビルド
ビルドを行います。
先程と同様にCompExample1/bin/Release/CompExample1.dll を ILSpy で逆コンパイルしてみましょう。
結果は以下です。

f:id:daigo-knowlbo:20170103115607p:plain

Resourcesが無くなりました。
そして、コードビハインドクラスの InitializeComponent() 内にXAMLで定義したUIを構築するためのコードが追加されています。つまり、XAMLはコードに変換され、ILとなりDLLアセンブリに格納されました。

開発時コンパイルのメリット

XAMLを開発時にコンパイルすると以下のようなメリットがあります。

1) コンパイル時にXAML構文チェックが行われる

例えば以下のようにXAML構文上のエラーがあった場合、XamlCompilation属性を使用していないとビルド(コンパイル)は正常終了してしまいます。そして実行時にエラーとなります。
開発ビルド時に構文チェックが行われることで、開発生産性の向上が見込まれます。

... 省略
   <Label Content="test" />   ← WPFは Content でOKでしたが、Xamarin Formsでは Text ですね。
... 省略

2) 実行時のパフォーマンスが高くなる

XAMLコンパイルするタイミングが開発ビルド時になるので、実行時にフォームを表示する際のパフォーマンスが向上します。