自動化(T4 Template)

現在、C→C#の移植作業をしています。

手作業でちまちましている箇所を、自動化できたらいいなと思いました。

そこで、構造体をクラスに移植する場合を考えます。

 


使用するもの

 

T4 テキスト テンプレート

 

参考<https://msdn.microsoft.com/ja-jp/library/bb126445.aspx>

 


方法

① テキストテンプレートを作成する。拡張子は.tt。

 

 【説明

 <#@ ディレクティブの指定 #>

 <#    複数のステートメント #>

   <#=  単一の式     #>

 <#+  関数やクラス   #>

 <#>がない箇所はそのまま作成される。

 


<#@ template debug="false" hostspecific="true" language="C#" #> ←ポイント①ファイルを読み込むときはhostspecific="true"にする
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace Test
{
<#
        string cName = string.Empty;
        foreach (var line in System.IO.File.ReadLines(this.Host.ResolvePath("Data.txt"), Encoding.GetEncoding("shift_jis")))
        {
                var s = line.Split('\t').ToList();
                if(cName == string.Empty)
                {
                     cName = s.First();
#>
    public class <#= cName #>
    {
<#              continue;
           }
 
                var data = new Data(s);
#>
                /// <summary>
                /// <#= data.Description #>
                /// </summary>
                public <#= data.Type #> <#= data.ItemName #> { get; set; }
 
<#
    }
#>
        }
}
 
<#+
    private class Data
    {
        // クラス名
        public string ClassName { get; set; }
        // 型
        public string Type { get; set; }
        // 項目名
        public string ItemName { get; set; }
         // 説明
        public string Description { get; set; }
private static Dictionary<string, string> TypDic = new Dictionary<string, string>()   { {"UD", "int"}, ←ポイント②定義しておいたら後で迷わなくてすむ {"DD", "uint"}, {"UW", "short"}, {"UB", "byte"}, };
public Data(List<string> s) {   Type = TypParse(s[0]);   ItemName = ItemParse(s[1]); Description = s[2]; } private string TypParse(string str) { string ret; if(!TypDic.TryGetValue(str, out ret)) { return "string"; } return ret; } private string ItemParse(string str) { return str.Substring(0, 1).ToUpper() + str.Substring(1).ToLower(); } } #>

② 新規ファイルを作成。一行目に作成したいクラス名、以降の行は型,項目名,説明をタブ区切りで定義する。

    (そもそもこれを作るのが面倒ということは置いておく...)

 

(例)

学生クラス
UD      id      学籍番号
char    name    氏名
UD      age     年齢
UD      schoolyear      学年

③ テンプレートを実行する。

  実行結果は下記のようになる。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Test
{
    public class 学生クラス
    {
                /// 
                /// 学籍番号
                /// 
                public int Id { get; set; }

                /// 
                /// 氏名
                /// 
                public string Name { get; set; }

                /// 
                /// 年齢
                /// 
                public int Age { get; set; }

                /// 
                /// 学年
                /// 
                public int Schoolyear { get; set; }

        }
}

まとめ

【メリット】

移植元ソースは独自の型が定義されているので、自分で確認して移植しないといけない。

ディクショナリーであらかじめ定義しておけば、悩まなくてすむし、忘れていても問題ない。

また、データが大量にある場合は、手作業で行うよりはミスが減るはず。

 

【デメリット】

移植元ソースをただ読み込むだけだと使用できないので、コピペしてファイルを作成しないといけない。

(読み込みを工夫したらいけますかね?)

結局そのひと手間が必要なので、自動化の道は遠い...。

現状関係ないが、エクセルを読み込むこともできるらしいので、エクセルの大量データを定義する場合は多少は使えるかもしれない。