やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

CodeDOMでソースコードを生成してみる

 クソダサいコードが出た。

成果物

CodeDOM

 C#にはCodeDOMというコード生成用ライブラリがある。

 今回はこれを使ってソースコードファイルを出力してみる。内容は名前空間とクラスだけ。

プロジェクト

dotnet new console -o CodeDomProp
cd CodeDomProp
dotnet add package System.CodeDom

コード

Program.cs

using System;
using System.IO;
using System.CodeDom;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Reflection; // TypeAttributes

namespace CodeDomProp
{
    class Program
    {
        static void Main(string[] args)
        {
            Generate(CreateCode());
        }
        // ファイル出力
        private static void Generate(CodeCompileUnit compileunit)
        {
            CSharpCodeProvider provider = new CSharpCodeProvider();
            string sourceFile = @"Hello.cs";
            using (StreamWriter sw = new StreamWriter(sourceFile, false))
            {
                IndentedTextWriter tw = new IndentedTextWriter(sw, "    ");
                provider.GenerateCodeFromCompileUnit(compileunit, tw,
                    new CodeGeneratorOptions());
                tw.Close();
            }
        }
        // コード生成
        private static CodeCompileUnit CreateCode()
        {
            CodeCompileUnit targetUnit = new CodeCompileUnit();
            CodeNamespace samples = new CodeNamespace("MyNamespace");
            samples.Imports.Add(new CodeNamespaceImport("System"));
            CodeTypeDeclaration targetClass = new CodeTypeDeclaration("MyClass");
            targetClass.IsClass = true;
            targetClass.TypeAttributes =
                TypeAttributes.Public | TypeAttributes.Sealed;
            samples.Types.Add(targetClass);
            targetUnit.Namespaces.Add(samples);
            return targetUnit;
        }
    }
}

出力結果

Hello.cs

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace MyNamespace {
    using System;
    
    
    public sealed class MyClass {
    }
}

 汚い……。以下のような不満点がある。

  • auto-generatedコメント不要
  • usingclassの間になぜ2改行?
  • {が改行されていない
  • public, sealdedの順序が指定できない(そういうもの?)

 以下のようなコードが欲しかったのに……。

using System;

namespace MyNamespace
{
    public sealed class MyClass
    {
    }
}

参考

分析

 どうやら以下のようにusingnamespaceの中に入るようにしか書けないっぽい。見たことないよそんなコード……。

namespace 名前空間
{
    using System;
}

 ふつうは以下。dotnet new console -o 名前でも以下のように出力される。

using System;

namespace 名前空間
{
}

 その上、自動実装プロパティなども使えないとか。問題が多すぎて使う気になれない。

ちょっと躓いたこと

 DLL参照を追加しないと以下のようなエラーになる。

error CS1069: 型名 'CodeCompileUnit' は名前空間 'System.CodeDom' に見つかりませんでした。この型はアセンブリ 'System.CodeDom, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' に転送されました。このアセンブリへの参照を追加することを検討してください。 

 DLL参照の追加は以下。

dotnet add package System.CodeDom

 TypeAttributesSystem.Reflectionusingでインポートすること。

所感

 思った以上に残念。

 気になったのは、ブロック開始{の前に改行+インデントがあるかないか。私は無い派だったのだが、C#dotnet newや、VisualStudioなどの自動生成は皆、改行+インデントするものが出力されていた。

 だからもういいやと諦めて改行する派に改宗しようと決心した。なのに、CodeDomでは逆に改行+インデントしないものが出力される……。クソが! 1字1句思い通りに書きたいんじゃ!

 やはり自分の思い通りに動いてくれるコードジェネレータが欲しい。

 CodeDOMは使うのやめよう。まだ仕様よく確認してないけど。以下の点だけで、もう思い通りのコードが書けないとわかったし。

  • C#2.0以降の構文に非対応
  • using System;などすべてnamespace配下に書かねばならない

 深く調査してまでして使う価値があるのか。調査するのも面倒。がんばって調査した結果、実現できないことが発覚したという屈辱を味わいたくない。それなら劣化車輪の再発明したい。

対象環境

$ uname -a
Linux raspberrypi 4.19.42-v7+ #1218 SMP Tue May 14 00:48:17 BST 2019 armv7l GNU/Linux