かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

使い捨てのT4テンプレート活用法

ということで、MSDNのT4テンプレートのドキュメントを見たのは、ちょろっと使おうと思ったからです。

単調な繰り返しを伴うコードを書くのは苦痛でしかありません。私が尊敬する上司の人は、これを田植えと言ってました。確かに。大事なお仕事ではあるんだけど、めんどくさいことは否めない。そんなときにT4 テンプレートが使えないかどうか、ちょっと考えてみるといいかも?というのが今回の趣旨です。

使ってみよう

実際に今から、T4 テンプレートを使ってコードを生成してみようと思います。今回はMicrosoft.Win32.OpenFileDialogをラップする感じのTriggerActionを作りたいのですが、OpenFileDialogが持つプロパティをチェックすると・・・

こんなにある!!まぁ手書きでもいけないことはないけど、今後SaveFileDialogとかにも対応していくことを考えると苦行になるかもしれないのでT4 テンプレートでさくっと作ってしまおうという魂胆です。

ということで早速やってみます。WPFアプリケーションのプロジェクトを作ってGenerator.ttという名前でテキストテンプレートを作ります。中身を試行錯誤した挙句、こういう感じで依存プロパティを定義できるようになりました。

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".generated" #>
<#@ assembly name="PresentationCore" #>
<#@ assembly name="PresentationFramework" #>
<#@ assembly name="WindowsBase" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Xaml" #>

<#@ import namespace="System" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Reflection" #>
<#@ import namespace="Microsoft.Win32" #>

<#
var props = typeof(OpenFileDialog).GetProperties();
foreach (var p in props)
{
	PropDp("OpenFileDialogAction", p);
}
#>

<#+
// 依存プロパティを作るお
void PropDp(string className, PropertyInfo p)
{
	var propertyType = p.PropertyType + (p.PropertyType.IsValueType ? "?" : "");
#>

public static readonly DependencyProperty <#= p.Name #>Property = 
	DependencyProperty.Register(
		"<#= p.Name #>",
		typeof(<#= propertyType #>),
		typeof(<#= className #>),
		new PropertyMetadata(null));

public <#= propertyType #> <#= p.Name #>
{
	get
	{
		return (<#= propertyType #>)this.GetValue(<#= p.Name #>Property);
	}
	
	set
	{
		this.SetValue(<#= p.Name #>Property, value);
	}
}
<#+
}
#>

生成されたコードをさくっとコピーしてOpenFileDialogActionに貼り付けます。コンパイルしてOK!かと思ったら1つのプロパティがIListのジェネリッククラスを使ってたので、そこでコンパイルがこけてしまいましたorz
生成されたコードは以下のようなものでした。

public static readonly DependencyProperty CustomPlacesProperty = 
	DependencyProperty.Register(
		"CustomPlaces",
		typeof(System.Collections.Generic.IList`1[Microsoft.Win32.FileDialogCustomPlace]),
		typeof(OpenFileDialogAction),
		new PropertyMetadata(null));

public System.Collections.Generic.IList`1[Microsoft.Win32.FileDialogCustomPlace] CustomPlaces
{
	get
	{
		return (System.Collections.Generic.IList`1[Microsoft.Win32.FileDialogCustomPlace])this.GetValue(CustomPlacesProperty);
	}
	
	set
	{
		this.SetValue(CustomPlacesProperty, value);
	}
}

これは、コンパイルできませんが汎用的なコードジェネレータを作るのが今回の目的ではないので、この部分は手書きでなおしました。とまぁ、こんな感じで使い捨てだけど、大量の単調なコードを書くことにT4 テンプレート使えるよ!!ということです。

何かの参考になれば・・・ということで今回はおしまい!