[Xamarin.Forms 1.5.1-pre1] XamlC (XAMLプリコンパイル)


## XamlCの概要

Xamarin.Forms 1.5.1-pre1がアナウンスされ、その中に __XamlC__ と呼ばれる機能が含まれています。

[Xamarin.Forms 1.5.1-pre1 Released - Xamarin Forums](https://forums.xamarin.com/discussion/52504/xamarin-forms-1-5-1-pre1-released)

以下の効果があります。

* 実行時にXAMLを解析しないことによるロード時間の減少
* アセンブリに.xmlファイルを含まなくなることによるアプリサイズの減少

XamlCの有効化は属性で指定します。

__アセンブリ全体で有効化：__
`[assembly:Xamarin.Forms.Xaml.XamlCompilation (Xamarin.Forms.Xaml.XamlCompilationOptions.Compile)]`

__クラス単位で有効化：__
`[Xamarin.Forms.Xaml.XamlCompilation (Xamarin.Forms.Xaml.XamlCompilationOptions.Compile)]`

__注意__
XamlCはβテスト版です、XF 1.5.1正式版までに外される可能性があります。(以前もPreview版に盛り込まれましたが「今はバグフィックスを優先する」と先送りにされました。)

### Xamarin.FormsにおけるXAML解釈

PageやViewの新規作成時にXAML付きのテンプレートを選ぶと、.xamlファイルとpartial指定付きの.csファイルが生成されます。そして、
実行時に.csファイルのコンストラクタ内の  `InitializeComponent()` で.xamlファイルをパースしViewを組み上げていくという仕組みです。

XamlCはコンパイル時にXAMLを解析し、相当するILを生成します。

### XamlCompilationOptions

ちなみに `XamlCompilationOptions` の定義はこうなっています。

```C#:
[Flags]
public enum XamlCompilationOptions
{
	Skip = 1,
	Compile = 2
}
```

`XamlCompilationOptions.Skip` はアセンブリ全体でXamlCを有効にしたうえで、特定のクラスだけプリコンパイルさせたくない時に使います。(多分)

## 試してみる

(※環境はMac＆Xamarin Studioです)

ソリューション作成から「Blank Xamarin.Forms App」で新規作成します。(今回はSharedにしました)

iOS、AndroidのプロジェクトのXamarin.Formsを1.5.1-pre1に更新します。(Preview版nugetパッケージへの更新方法は[以前の記事](http://qiita.com/P3PPP/items/b4db6fa93e6ccc55fc9a#xamarinforms-%E3%81%AE%E6%9B%B4%E6%96%B0)にスクリーンショット付きの手順があります。)

Sharedプロジェクトに新しいContentPage(XAML)を追加します。

`App` クラスを修正します。

`MainPage = new MyPage();`

### 本当にプリコンパイルされているか？

`Label`のスペルをわざと間違えて"Lable"にしてみます。

```xml:
	<ContentPage.Content>
		<Lable Text="Hello XamlC!"/>
	</ContentPage.Content>
```

.csファイルにはXamlCompilation属性を指定します。

```C#:	
	[Xamarin.Forms.Xaml.XamlCompilation (Xamarin.Forms.Xaml.XamlCompilationOptions.Compile)]
	public partial class MyPage : ContentPage
```

ビルドすると...

![スクリーンショット 2015-10-05 19.41.38.jpg](https://qiita-image-store.s3.amazonaws.com/0/40121/a75ca527-64bb-fe2b-ae6a-7ee0f6764102.jpeg "スクリーンショット 2015-10-05 19.41.38.jpg")

おお！コンパイル時点でエラーになりました。ちゃんとXamlCが働いているようです。


#### おまけ：存在しないプロパティ

`Label`の部分を少し修正してみました。

```xml:
<Label Text="Hello XamlC!" ForeColor="#FF8888" />
```

正しいプロパティ名は`TextColor`で"ForeColor"というプロパティはLabelクラスには存在しません。

__結果__

エラー無くコンパイルされ、実行時例外も発生しませんでした。これはAttached Property(例 `Grid.Row`)のようにコントロールに存在しないプロパティが書かれる場合があるため、エラーにできていないように思えます。

...でもXamlCオフだと実行時例外なんですよね、コレ。

#### おまけ：間違ったプロパティ値の指定

今度は値の方を間違えてみました。`TextColor`をWebカラー式に指定しようとして、先頭の「#」が抜けています。

```xml:
<Label Text="Hello XamlC!" TextColor="FF8888" />
```

__結果__

実行時例外となりました。これは `IValueConverter.Convert()` を呼び出すILに変換されたため、実行時にstringからColorへ変換できずに例外発生になったものと思われます。
プリコンパイル時にダイレクトに代入するコードに変換してくれるようになるといいですね。(オーバーヘッドも減るし、間違いにも気付けるので)

### 速度的な話

肝心の高速化の検証。

.xaml側はこう、

```xml:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="XamlC.MyPage">
	<ContentPage.Content>
		<Label />
	</ContentPage.Content>
</ContentPage>
```

.csは `InitializeComponent()` にかかった時間を計測します。

```C#:
public MyPage()
{
	var stopwatch = Stopwatch.StartNew();
	InitializeComponent();
	stopwatch.Stop();
	((Label)Content).Text = stopwatch.ElapsedMilliseconds + "ms";
}
```

ほいで計測手順と環境

* 端末はiPhone 5(iOS 8.4.1)
* Debugビルドで実機に転送、デバッグ自体はすぐに停止。
* 実機に転送されたアプリを起動。
* Labelに表示された結果を確認後、アプリを終了させる。
* 10回ずつ計測。

__結果__

* XamlC無効
  * 平均： 103.9 ms
  * 個別： 105, 103, 101, 104, 102, 105, 106, 110, 102, 101 (ms)
* XamlC有効
  * 平均： 19 ms
  * 個別： 19, 18, 36, 16, 16, 19, 16, 16, 18, 16 (ms)

`InitializeComponent()` にかかる時間がおよそ1/5に短縮されました！
かなり効果があると言えるでしょう。

複雑なXAMLであればより大きな差になるでしょう。しかし、レイアウト自体は実行時に行われるため、座標計算などのオーバーヘッドにはXamlCの効果が及ばない点に注意が必要です。


#### おまけ：C#で書いた場合

ついでにC#オンリーで書いた場合のタイムも計測してみました。

```C#:
public CsPage()
{
	var stopwatch = Stopwatch.StartNew();
	Content = new Label();
	stopwatch.Stop();
	((Label)Content).Text = stopwatch.ElapsedMilliseconds + "ms";
}
```

__結果__

* C#オンリー
  * 平均： 15.6 ms
  * 個別： 15, 15, 15, 15, 19, 15, 15, 15, 16, 16 (ms)

XamlCを有効にした場合よりも少し速いのはコードの違いか、何かしら最適化が働いているのか...


## まとめ

* 高速化はかなり有効、C#で書いた場合に迫る速度になる。
* XAML記述サポートとして期待するにはもう一歩踏み込んだ解析能力が必要。(XamlC本来の目的ではない)
* ぶっちゃけ全部C#で書くと速い。


## 参考

[Xamarin.Forms 1.5.1-pre1 Released - Xamarin Forums](https://forums.xamarin.com/discussion/52504/xamarin-forms-1-5-1-pre1-released)

[Xamarin.Forms 1.4.3-pre2 Released - Xamarin Forums](https://forums.xamarin.com/discussion/38729/xamarin-forms-1-4-3-pre2-released/p1)