C#→JavaScriptコード変換ってどうよ? DuoCodeを試してみた連載:「○○してみた」日記

C#コードをJavaScriptに変換してくれるDuoCode。まだベータ版ではあるが、サンプルコードを基にその能力を調べてみた。

» 2015年02月13日 11時32分 公開
[一色政彦デジタルアドバンテージ]
連載:「○○してみた」日記
Insider.NET

 

「連載:「○○してみた」日記」のインデックス

連載目次

 本連載は、Insider.NET編集部のスタッフが「これ気になるな〜」と思った技術的な話題などを、実際に手を動かして試し、その情報を読者と共有することをコンセプトとしている。

 ちなみに筆者は、長らく担当してきた@ITのInsider.NET編集長を今年から卒業し、今は一編集スタッフとしてInsider.NETに関わっている。現在の新編集長は、すでに2年ほどInsider.NETで活躍してきた「かわさき しんじ」氏だ。かわさき氏は、本連載を含め、Insider.NETの新たな成長に向けていろいろと考えているようなので、ぜひご期待いただきたい。なお私自身は、WebやIoTやスマホ開発技術など幅広い最新技術情報を取り扱う新興Webメディアの「Build Insider」で2年近く編集長をしているので、「そのサイト、知らない・聞いたことがない」という方は、この記事を機に、ぜひ情報収集先リストの端に加えていただけると幸いである。

 さて少し脱線し過ぎたが、ところであなたは、Webアプリなどで使うJavaScriptコードを、C#コードから変換すると、便利だと思うだろうか?

 先月中旬ごろだっただろうか。「“DuoCode”というC#→JavaScriptコンパイラーが出てくるよ」という情報をキャッチした。本稿では、このDuoCodeを試し、そのリポートをコンパクトにまとめる。

それ誰トクなのよ?!

 DuoCodeのことを聞いてすぐに、Java→JavaScriptのコード変換ができる“Google Web Toolkit”(以下、GWT)のことを思い出した。その登場は2006年でもうすぐ9年になる。登場から数年後に聞いた開発者の評価は、「純粋にJavaScriptを書くよりも、最適化された良いコードが生成される」というものだった。「コード変換の有効性」に半信半疑だった筆者にとっては、がぜん興味が湧いたことは言うまでもない。

 しかし結局、筆者は手を出さなかった。そもそも筆者はC#好き人間だったので(というか2007年からずっとMicrosoft MVP for C#である。今年から「C#」カテゴリは「.NET」カテゴリに統合された)、あえてJavaコードを書きたくもなく、むしろJavaScriptコードを直接書く方が実務面では良かったからだ。そうこうするうちに、CoffeeScriptの次にTypeScriptまでも登場し、GWTを使う気は一切なくなった。

 そんな心持ちになったところに、「DuoCodeでC#→JavaScript変換しようよ」という話である。当然、「ええっ?! 何で今頃? 何のために?」と思ってしまった……。しかし「先入観にとらわれず、とにかく触るだけ触ってから、考えてみよう」と、ベータ版の招待に申し込んでみた(下記のリンク先の[EMAIL]欄にメールアドレスを入力して[Subscribe]ボタンをクリックすると、招待に申し込める。どれくらい待たされるのかは不明だが、2週間ぐらいはかかると思う。ちなみにベータ版は2015年6月30日まで使える。製品版登場時には有償になる模様)。

図1 DuoCodeの公式サイト 図1 DuoCodeの公式サイト

DuoCodeとは?

 上の公式サイトを見ると、英語で「あなたのC#コードとスキルを、DuoCodeでWebに持っていこう」というキャッチコピーが書かれている。確かにこれまでに積み上げてきたC#の既存資産をJavaScript化できるのは大きいかもしれない。しかしどれくらいの精度で移植できるのかは気になるところだ。

 次に、「Microsoftの“Roslyn”の力で実現している、C#→JavaScriptコンパイラー」という記載がある。この変換エンジンが実現できたのは、どうやら次期コンパイラーの“Roslyn”が登場したからのようだ。“Roslyn”によってマイクロソフト以外でもコンパイル関連サービスが柔軟に実現できるようになるとされていたが、早くもそんなサービスが登場してきたということだ。あらためて“Roslyn”登場のメリットが感じられた。

 その他、公式ページから読み取れる、DuoCodeの特徴を箇条書きで示しておこう。

  • C#言語、しかも6.0が使える
  • 開発生産性が高い、最強のIDEであるVisual Studioが使える
  • .NET Framework標準ライブラリで慣れ親しんだクラスが使える
  • C#から変換されたJavaScriptコードには可読性がありメンテナンスしやすい

 これだけ見ると「すごい」と言わざるを得ない。いやまだ信用しないぞ。早速、DuoCodeをインストールして試していこう。

DuoCodeのインストール

 図2のような招待メールが届けば、DuoCodeを試用できる。そのメール上の[Download Now]ボタンをクリックして、インストーラー(本稿の例では「duocode0.3.878.0beta.exe」ファイル)をダウンロードする。

図2 DuoCodeの招待メール 図2 DuoCodeの招待メール

 インストーラーを実行すると、図3のようなウィザードが表示される。特に難しいところもなく、簡単にインストールできる。

図3 DuoCodeのインストーラー 図3 DuoCodeのインストーラー

Visual Studio用のDuoCodeプロジェクトテンプレート

 インストールが完了したら、Visual Studio(2012/2013/2015 CTP 5)を起動して[新しいプロジェクト]ダイアログを表示し、図4のように左側のツリーから[Visual C#]−[DuoCode]を選択しよう。

図4 DuoCodeのプロジェクトテンプレート 図4 DuoCodeのプロジェクトテンプレート

 図4の右側を見ると、以下の六つのプロジェクトテンプレートがインストールされている。

  • HelloDuoCode (HTML Application): C#コードのサンプルHTMLアプリ(JavaScriptコードに変換される)
  • Class Library: C#クラスライブラリを作成するためのプロジェクト(JavaScriptコードに変換される)
  • UnitTest (HTML Application): 単体テストとQUnit.jsテスティングフレームワークが含まれる、C#コードのサンプルHTMLアプリ(JavaScriptコードに変換される)。
  • WebGL (HTML Application): C#コードのサンプルWebGLアプリ(JavaScriptコードに変換される)
  • RayTracer (HTML Application): C#コードのサンプルRayTracerアプリ(JavaScriptコードに変換される)
  • TicTacDuo Game (Console + HTML Application): C#コードのTicTacDuoゲーム(ネイティブの.exeコンソールアプリか、JavaScriptコードのいずれかに変換される)。コンソールアプリではなく、HTMLアプリとして実行したい場合には、ビルド対象となるプロジェクト構成で「JavaScript」を選択する必要がある

 本稿では、最も基本的と思われる「HelloDuoCode (HTML Application)」テンプレートで新規プロジェクトを作成してみよう。

Visual Studio用のDuoCodeプロジェクトテンプレート

図5 HelloDuoCodeプロジェクトで生成されるひな型のソースファイル群 図5 HelloDuoCodeプロジェクトで生成されるひな型のソースファイル群

 図5はプロジェクト作成後の[ソリューション エクスプローラー]の中身だ。以下のようなファイルが生成されている。

  • console.css: Webページ用のCSSファイル
  • index.html: HTMLアプリを表示するためのWebページ
  • Program.cs: HTMLアプリのプログラム本体
  • web.config: ソースマップデバッグするためのIIS Express用の構成ファイル

 それぞれのファイル内容を見ていく前に、ひな型の状態での実行結果を見ておこう。図6のようになる。

図6 HelloDuoCodeアプリの実行例 図6 HelloDuoCodeアプリの実行例

HelloDuoCodeアプリのコード内容

 console.cssファイルとweb.configファイルの中身は、プログラム内容そのものではないので説明を割愛する。index.htmlファイルから順に見ていこう。

index.htmlファイル: HTMLアプリを表示するためのWebページ

<!DOCTYPE html>
<html>
<head>
  <title>Hello DuoCode</title>
  <link href="console.css" rel="stylesheet" type="text/css">

  <!-- アセンブリ参照(依存関係順に並べる) -->
  <script type="text/javascript" src="scripts/mscorlib.js"></script>
  <script type="text/javascript" src="scripts/HelloDuoCode.js"></script>

</head>

<body onload="HelloDuoCode.Program.Run()">

  <h1>Hello DuoCode</h1>

  <div id="content"></div>

  <div id="duocode-console">
    <font color="darkblue">
      This is a sample of a project written in C# and compiled to JavaScript using DuoCode.
      Press F12 to see the source code of the project.
      Enable source-maps in your browser and you'll be able to debug the C# code directly.
      You can also debug it from Visual Studio 2015.
    </font>
    <hr />
  </div>
</body>
</html>

リスト1 index.htmlファイルの内容

 気になるのは、「アセンブリ参照」と書かれているところだ。その記述を読むと、「scripts」フォルダー内に「mscorlib.js」と「HelloDuoCode.js」の二つのファイルが存在するのが分かる(これらは[ソリューション エクスプローラー]で[すべてのファイルを表示]ボタンをクリックすると表示される)。実際にフォルダー内を見ると、図7のようになっていた。

図7 HelloDuoCodeアプリの実行例 図7 HelloDuoCodeアプリの実行例

 このうちの.jsファイルは、DuoCodeによりC#コードからJavaScriptコードに変換された後のファイルだ。各ファイルは、以下のような役割を持つ。

  • HelloDuoCode.js: HelloDuoCodeプロジェクトのC#コードをビルドすると生成される。プロジェクトの[アセンブリ名]を変更すると、このファイル名も変わる
  • HelloDuoCode.js.map: C#コードとJavaScriptコードのマッピング情報を持つソースマップファイルで、主にデバッグ時に使われる
  • mscorlib.dll: DuoCodeが提供するアセンブリで、.NET Frameworkの標準ライブラリと互換性のあるクラス群を含む(詳細後述)
  • mscorlib.js: 上の「mscorlib.dll」ファイルから作成されたJavaScriptコードのファイル

mscorlib.dll: DuoCodeが提供する.NET Frameworkの標準ライブラリ

 前掲の[ソリューション エクスプローラー](図5)を見ると、[参照設定]に「mscorlib」というアセンブリが追加されている。このファイルの実体は、(Windows 8.1(64bit)上にインストールした筆者の環境では)「C:\Program Files (x86)\DuoCode\mscorlib.dll」にあり、これが[ローカル コピー]される設定になっている。つまりこれが、上の「scripts」フォルダーに出力されていると考えられる。

 そのmscorlibアセンブリをVisual Studioの[オブジェクト ブラウザー]で参照したのが図8である。

図8 mscorlibアセンブリの内容 図8 mscorlibアセンブリの内容

 上の方には「DuoCodeほにゃらら」という名前空間もあるが、その下のほとんどは「Systemほにゃらら」という.NET Framework標準ライブラリと互換性のある名前空間になっている。一番下の「System.Web」名前空間の中には「HttpUtility」クラスがあり、右側を見ると「UrlDecode(String)」メソッドと「UrlEncode(String)」メソッドが含まれているようである。このように「.NETのライブラリ」といっても、かなり制限された一部のメソッドだけが使えるようだ(.NETライブラリの「サブセット」のようなものだが、DuoCodeの公式ページではそのようには説明されていないので、「部分的に互換性のあるクラス群」と表現すればよいのだろうか)。

 なお、DuoCodeが提供するクラス群は、今後、きちんとしたAPIリファレンスが提供されるものと思われるが、現時点では[オブジェクト ブラウザー]を使うなどして、使用可能なクラスやメソッドなどを調べながら使う必要があるだろう。

index.htmlページから呼び出されるプログラムのコード

 再び、リスト1を参照してほしい。<body>タグのonloadイベント属性に「HelloDuoCode.Program.Run()」というJavaScriptコードが記載されている。ここでC#で記述したプログラムのコードが呼び出されている。そこで次に、Program.csファイルの中身を見てみよう。

Program.csファイル: HTMLアプリのプログラム本体

using System;
using DuoCode.Dom;
//using static DuoCode.Dom.Global; // C# 6.0の「using static」を使うには、このコメントを外す

namespace HelloDuoCode
{
  static class Program
  {
    public class Greeter
    {
      private readonly HTMLElement element;
      private readonly HTMLElement span;
      private int timerToken;

      public Greeter(HTMLElement el)
      {
        element = el;
        span = Global.document.createElement("span");
        element.appendChild(span);
        Tick();
      }

      public void Start()
      {
        timerToken = Global.window.setInterval((Action)Tick, 500);
      }

      public void Stop()
      {
        Global.window.clearTimeout(timerToken);
      }

      private void Tick()
      {
        span.innerHTML = string.Format("The time is: {0}", DateTime.Now); // try to put a breakpoint here
      }
    }

    static void Run() // HTML <body>タグonloadイベント属性に指定したエントリポイント(index.htmlファイルを参照)
    {
      System.Console.WriteLine("Hello DuoCode");

      var el = Global.document.getElementById("content");
      var greeter = new Greeter(el);
      greeter.Start();
    }
  }
}

リスト2 Program.csファイルの内容

 前述のJavaScriptコードから、リスト2のRunメソッドが呼び出されることになる。

 そのRunメソッドの中で、まずSystem.Console.WriteLineメソッドが呼ばれている。このコードが呼ばれると、メソッド引数の文字列が(リスト1の)<div id="duocode-console">要素の一番下に(テキストではなく)HTMLコードとして追加される(例えば「System.Console.WriteLine("<b>Hello DuoCode</b>")」のように<b>タグを追加すれば、HTMLページ上で太字になる)。

 次にGlobal.document.getElementById("content")メソッドで、(リスト1の)<div id="content">の要素を取得している。HTML&JavaScript開発経験があれば分かると思うが、JavaScriptを使って操作するDOMのdocument.getElementByIdメソッドに相当するものである。このようにGlobalクラス(DuoCode.Dom名前空間)を使えば、DOMも操作できる。

 さらにその下にあるGreeterクラスは独自に実装したクラスである。このように自作のクラスを使って、さまざまな処理が実現できることが分かる。このGreeterクラス内は、C#とJavaScript開発経験があれば難しくないと思うので、説明を割愛する。

HelloDuoCode.jsファイル: C#から変換されたJavaScriptコード

 最後に「変換後のコードはメンテナンスしやすい」という特徴が本当なのかを確認しておこう。

//
// HelloDuoCode, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
//
// Generated by DuoCode Compiler 0.3.878.0 BETA
//
(function HelloDuoCode() {
"use strict";
var $asm = {
  fullName: "HelloDuoCode",
  anonymousTypes: [],
  types: [],
  $getAttrs: function() {
    return [new System.Reflection.AssemblyTitleAttribute.ctor("HelloDuoCode"), new System.Reflection.AssemblyDescriptionAttribute.ctor(""), 
      new System.Reflection.AssemblyConfigurationAttribute.ctor(""), new System.Reflection.AssemblyCompanyAttribute.ctor(""), 
      new System.Reflection.AssemblyProductAttribute.ctor("HelloDuoCode"), new System.Reflection.AssemblyCopyrightAttribute.ctor("Copyright \xA9  2015"), 
      new System.Reflection.AssemblyTrademarkAttribute.ctor(""), new System.Reflection.AssemblyCultureAttribute.ctor(""), 
      new System.Reflection.AssemblyVersionAttribute.ctor("1.0.0.0"), new System.Reflection.AssemblyFileVersionAttribute.ctor("1.0.0.0"), 
      new DuoCode.Runtime.CompilerAttribute.ctor("0.3.878.0 BETA")];
  }
};
var $g = (typeof(global) !== "undefined" ? global : window);
var HelloDuoCode = $g.HelloDuoCode = $g.HelloDuoCode || {};
var $d = DuoCode.Runtime;
$d.$assemblies["HelloDuoCode"] = $asm;
HelloDuoCode.Program = $d.declare("HelloDuoCode.Program", System.Object, 0, $asm, function($t, $p) {
  $t.Run = function Program_Run() {
    System.Console.WriteLine$10("Hello DuoCode");
    System.Console.WriteLine$10("<p>Hello DuoCode2</p>");

    var el = document.getElementById("content");
    var greeter = new HelloDuoCode.Program.Greeter.ctor(el);
    greeter.Start();
  };
});
HelloDuoCode.Program.Greeter = $d.declare("Greeter", System.Object, 0, HelloDuoCode.Program, function($t, $p) {
  $t.$ator = function() {
    this.element = null;
    this.span = null;
    this.timerToken = 0;
  };
  $t.ctor = function Greeter(el) {
    $t.$baseType.ctor.call(this);
    this.element = el;
    this.span = document.createElement("span");
    this.element.appendChild(this.span);
    this.Tick();
  };
  $t.ctor.prototype = $p;
  $p.Start = function Greeter_Start() {
    this.timerToken = window.setInterval($d.delegate(this.Tick, this), 500);
  };
  $p.Stop = function Greeter_Stop() {
    window.clearTimeout(this.timerToken);
  };
  $p.Tick = function Greeter_Tick() {
    this.span.innerHTML = String.Format("The time is: {0}", $d.array(System.Object, [System.DateTime().get_Now()])); // try to put a breakpoint here
  };
});
return $asm;
})();
//# sourceMappingURL=HelloDuoCode.js.map

リスト3 HelloDuoCode.jsファイルの内容

 読者の皆さんは、どう感じるだろうか。筆者の感想としては、「このコードを編集する」となった場合、「できなくはない」が「できればしたくない」といったところだ。

【コラム】ビルド関連の機能について

 DuoCodeは、Visual Studioなどのビルドで活用されるMSBuildタスクに対応しているだけでなく、C#のcsc.exeコマンドと同じような「dcc.exe」というコマンドライン用のコンパイラーツールが用意されている(インストール先の例:「C:\Program Files (x86)\DuoCode\dcc.exe」)。


総括

 以上、DuoCodeの基本機能を試した。他にもサンプルがいくつかあるが、それなりに文章が長くなったので、本稿はここで終わりとする。気になる方は、ぜひ自分で試してみてほしい。

 で、筆者が「DuoCodeを使いたい」と思ったかどうかだが、依然として「微妙」な気持ちだ。かなり悩む。やっぱり「TypeScriptにしておくべきではないか」などと思ってしまう。

 確かに.NETのクラスライブラリと共通のクラスが存在するのは魅力的だ。しかし全てのメソッドが使えるわけではなく、かなり機能が制限されているというのは、実用上でどれくらいデメリットになるのかが気に掛かる。

 もちろんTypeScript開発よりも、DuoCode開発の開発生産性が大幅に高いのであれば、選択候補に挙げたいとは思うのだが……。あと、有償製品になりそうなので、その金額も懸念材料だ。

 いずれにしてもDuoCodeはまだベータ版なので、決定的な評価を下すには早すぎるだろう。取りあえず今後も、その動向に注視していきたい。

「連載:「○○してみた」日記」のインデックス

連載:「○○してみた」日記

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。