2018年02月07日

[Event] 「BuriKaigi2018」 (2018年2月3日)を開催しました

BuriKaigi2018 集合写真

富山県氷見市で、毎年恒例の勉強会「BuriKaigi2018」を開催しました。

78名もの方に参加していただきました。 雪が多い季節にも関わらず、今年も遠方からも多くのスピーカーや参加者の方がいらしてくださいました。 本当にありがとうございました。

当日の盛り上がりは Twitter などで見ることができます。

開催概要

BuriKaigi2018
日時 2018/03/02(土) 13:00-18:30 (終了後懇親会)
会場
氷見いきいき元気館 (富山県氷見市)
会場となった氷見いきいき元気館
主催・共催 Hokuriku.NET
北陸エンジニアグループ
スポンサー Forkwell
詳細 BuriKaigi2018 | connpass
スピーカー陣
スピーカー陣
受付付近
受付付近

内容

.NET トラック
.NET側の会場の様子
.NET側の会場の様子
タイトル 概要 スピーカー
「Visual Studio 2017 新機能wrap up」 VS2017が登場しておよそ1年が経過したので、リリース後に追加された新機能をまとめてみましょう。 森 博之 氏 @hiroyuki_mori
Microsoft MVP for Visual Studio and Development Technologies
森 博之 氏
「Introduction to Xamarin.Forms for MacOS - あるいは UWP から Mac App への布石 -」 初めてXamarin.Formsを触ってみたのが2014年3月。
UWP、Android、iOSの他にPreviewでMacOSがあるらしい? ので、ちょっと触ってみたのゆるふわセッション。
蜜葉 優 氏 @mitsuba_yu
Microsoft MVP for Windows Development
蜜葉 優 氏
「ItemsStackPanelの気持ちに寄り添ってパフォーマンスを改善」 UWPのListViewは ItemsStackPanelがよしなに仮想化してくれます。
ところが、場合によってはうまく仮想化してもらえません。
ItemsStackPanelの気持ちの寄り添って、パフォーマンスを改善してみます。
@tmyt
Microsoft MVP for Windows Development
「コントロールベンダー視点での Command Line Interface (CLI)」 ここ数年?の CLIの隆盛?を UIコンポーネントベンターの一員として10年間在籍している中から考えます。 池原 大然 氏 @Neri78
池原 大然 氏
「デスクトップ アプリがこの先生きのこるには 2018」 ここ1年間、著名なデスクトップ アプリがWindowsストアに並ぶなど、UWP以外のWindowsアプリ開発を取り巻く環境が変化しました。
2017年を振り返りつつ、デスクトップ アプリ開発の立ち位置について考えてみます。
@Grabacr07
Microsoft MVP for Windows Development
Microsoft MVP for Visual Studio and Development Technologies
「Windows Serverの新しい管理ツールの紹介」 Windows Serverは、Server Coreで提供されます。
Server Coreは、コンパクトで身軽な反面デスクトップエクスペリエンスが提供されないため手軽なサーバー管理が難しくなります。
オンプレミスの環境であれば、RSATなどを利用できますが、クラウド環境では難しい状況あります。
これらの問題を解決する新しいWebベースのサーバー管理ツール (Honolulu プロジェクト) について紹介します。
澤田 賢也 氏 @masayasawada
Microsoft MVP for Cloud and Datacenter Management
澤田 賢也 氏
「見える化、言える化、やりきれる化! Dynamics365 北陸へ拡散」 吉島 良平 氏 @navisionaxapta
Microsoft MVP for Business Solutions

杉本 和也 氏 @sugimomoto
Microsoft MVP for Business Solutions
吉島 良平 氏と杉本 和也 氏
「C#大好きMVPによるドキドキ ライブ コーディング」 石野 光仁 氏 @ailight
Microsoft MVP for Visual Studio and Development Technologies

鈴木 孝明 氏 @xin9le
Microsoft MVP for Visual Studio and Development Technologies

小島 富治雄 氏 @Fujiwo
Microsoft MVP for Visual Studio and Development Technologies

室星 亮太 氏 @ryotamurohoshi
Microsoft MVP for Visual Studio and Development Technologies
石野 光仁 氏、小島 富治雄 氏、鈴木 孝明 氏、室星 亮太 氏 小島 富治雄 氏、鈴木 孝明 氏、室星 亮太 氏 小島 富治雄 氏、鈴木 孝明 氏、室星 亮太 氏 石野 光仁 氏、小島 富治雄 氏、鈴木 孝明 氏、室星 亮太 氏
Java 他トラック
タイトル 概要 スピーカー
「JavaOne 2017フィードバック - JDKリリースモデル変更とJava EEのEclipse Foundationへの移行」 昨年10月初旬に開催されたJavaOne 2017はJava SE 9とJava EE 8が大きなテーマでしたが、開催に合わせて重要な発表が2つありました。
このセッションでは、その2つの発表 - JDKリリースモデル変更とJava EEのEclipse Foundationへの移行、EE4Jプロジェクトの設立について、その内容をご説明します。
今後Javaの活用がどう変わるのかがわかります。
伊藤 敬 氏@itakash
Oracle
「モジュール移行の課題と対策」 Java SE 9で導入されたProject Jigsawにより、JARファイルの扱いの煩雑さから解放されました。
その一方でモジュールを使用していないアプリケーションであっても、モジュール導入の影響によりビルドできなかったり、動作しなかったりする問題があります。
また、モジュールを導入するにしても、現状ではモジュールに対応したライブラリと非モジュールのライブラリを併用しなくてはなりません。
本セッションではモジュールについて説明し、モジュールを使用する際の課題とそれに対する対応についても解説していきます。
櫻庭 祐一 氏@skrb
「Running Kubernetes on Azure(仮)」 Azure 上で Kubernetes の動かし方 Docker などのコンテナに興味のある方向け。 山本 誠樹 (やまもと まさき) 氏 @nnasaki
Microsoft MVP for Microsoft Azure
「日本語入力の落とし穴」 日本語を受け取るアプリケーションがおちいりがちな落とし穴について紹介する。
「テキスト入力をハンドリングするアプリケーションを作る人」「日本語入力がうまくできないアプリケーションを修正したい人」「独自の日本語入力を作りたい人」などに役に立つと思う。
@mzp
日本語入力の落とし穴 #burikaigi | みずぴー日記
「JetBrainsでもっと楽しくコーディング、ワークフロー」 IDEベンダとして有名なJetBrainsですが、IDEのみならずチームツールもリリースしています。
JetBrainsのIDEはもちろんのこと、10名までは無償で使えるチームツールを組み合わせたワークフローまでご紹介いたします。
山本ゆうすけ氏 @yusukey
株式会社サムライズム
「Excel芸2018」 Scalaは関数型プログラミングもできる、マルチパラダイムなプログラミング言語です。
本発表では、関数プログラミングによって何ができるようになるかを、SIerにとってなじみが深いであろう話題を交えて紹介します。
Excel絶対殺すマン @bleis
「Asciidocの紹介」 文書は気楽に記述でき、バージョン管理しやすいと便利です。
今回はソフトウェア開発者が記述するような文書のために開発されたAsciidocというツールを紹介します。
Javaさえ入っていれば使えるので、Windowsでも気軽に使うことができます。
セッションでは、環境構築から文法、そして便利な周辺ツールなどを紹介します。

Learning Object

Asciidocによる文書管理
Groovyによるハック
@kyon_mm
LT (ライトニングトーク)
タイトル スピーカー
スポンサービデオ Forkwell
スポンサーのForkwell様
リモートデスクトップエンジニアの生態2018 金子雄一 氏 @oskaneko
Microsoft MVP for Enterprise Mobility
金子雄一 氏
Office 365ってな~に?
最新のWindowsでOffice製品を適切に使うために知っていて欲しい3つのこと
さくしま たかえ 氏 @RamuMystery
Microsoft MVP for Windows and Devices for IT
Windows Insider MVP
さくしま たかえ 氏
データプラットフォーム最新情報をざっくり 山本 美穂 氏 @mihochannel
日本マイクロソフト株式会社
山本 美穂 氏
とあるマーケティング部隊とエンジニアとScalaの導入 @grimrose
とあるマーケティング部隊とエンジニアとScalaの導入 | GitHub

懇親会

刺身の船盛
刺身の船盛
ぶりしゃぶ
ぶりしゃぶ

翌日のUFOツアー

翌日の観光ツアーで行ったコスモアイル羽咋
翌日の観光ツアーで行ったコスモアイル羽咋

参加レポート

2017年12月23日

[C#][Joke] Playing C♯

C丼

C# Advent Calendar 2017 の12月23日の記事。

今回は、C# で C♯ を演奏してみた、と言いたいだけの記事。

C♯

プログラミング言語である C# は、通常 "C#" と表記し "c sharp" と読む。

※ "C" を進化させた "C++" を更に進化させたという意味の "C++++" が由来、というのも有名な話だ。 これは、C# の父である Anders Hejlsberg 氏から直に聞いたので間違いない。

C++++

ご存知の方が多いと思うが、"C#" の # は通常「ナンバーサイン」とか「番号記号」とか「井桁」とか呼ばれるもので、英語圏でも "number" とか "pound" とか "hash" などと発音される。 音楽記号である(シャープ)とは別の文字だ。

そして、筆者はよく音楽の演奏をするのだが、C(シー)(シャープ) と書かれていれば、それは嬰ハ長調、または、嬰ハの音 (ハ長調のド) を表していることが多い。

嬰ハ長調
C♯

というわけで (謎)、C# で C♯ を演奏してみたい。

C# で C♯ を鳴らす

C# で音階を演奏するため、2つの方法を試してみる。

C# で C♯ を鳴らす (Beep)

一つ目は、System.Console.Beep(int frequency, int duration) を用いる方法だ。 このメソッドは、音の周波数と長さを渡して演奏するようになっている。

C# で 音名 C♯ の音を鳴らす (Beep)

音は1オクターブ上がると周波数が2倍になる。平均率の場合、音階の中の全部の半音は同じだけ音が上がっていく。 1オクターブは半音12個分だから、半音上がるごとに周波数は「2の1/12乗」倍になる計算だ。 全音上がるごとだと「2の1/6乗」倍。

なので、C# で音名 C♯ の四分音符を演奏すると、例えば次のようになる。

using System;

static class MusicalScale
{
    public static double A4Frequency { get; set; } = 440.0; // ベースとなる音 A4 (デフォルトは440Hz)

    // 平均律 (equal temperament) | Wikipedia
    // https://ja.wikipedia.org/wiki/%E5%B9%B3%E5%9D%87%E5%BE%8B
    // 1オクターブ (6音) で周波数が2倍になり、半音ごとに周波数は「2の1/12乗」倍になる
    public static double EqualTemperamentFrequency(double relativePositionInScale /* ベースとなる音 A4 から何音上か */)
        => A4Frequency * System.Math.Pow(2.0, relativePositionInScale * 2.0 / 12.0);
}

// 音符の長さ
enum Duration
{
    Whole     = 1600       , // 全音符
    Half      = Whole   / 2, // 二分音符
    Quarter   = Half    / 2, // 四分音符
    Eighth    = Quarter / 2, // 八分音符
    Sixteenth = Eighth  / 2, // 十六分音符
}

class Program
{
    const double relativePositionOfCSharp = -4.0; //  C♯(嬰ハ) は A(イ) より4音下

    static void Main()
    {
        PlayCSharpNote();

        Console.ReadKey();
    }

    // 音名  C♯ の音を演奏 (Beep で)
    static void PlayCSharpNote()
        => Beep(frequency: MusicalScale.EqualTemperamentFrequency(relativePositionOfCSharp), duration: Duration.Quarter);

    // 指定された周波数/長さの音を鳴らす
    static void Beep(double frequency, Duration duration)
        => Console.Beep(frequency: (int)System.Math.Round(frequency), duration: (int)duration);
}

実行すると、C♯ が聞こえてくる。

C# で C♯ の音階を平均率で演奏する (Beep)

この要領で、平均律で C♯ の音階 (嬰ハ長調のドレミファソラシド) を演奏してみよう。

using System;
using System.Collections.Generic;

static class EnumerableExtentions
{
    public static void ForEach<T>(this IEnumerable<T> @this, Action<T> action)
    {
        foreach (var element in @this)
            action(element);
    }
}

static class MusicalScale
{
    public static double A4Frequency { get; set; } = 440.0; // ベースとなる音 A4 (デフォルトは440Hz)

    // 平均律 (equal temperament) | Wikipedia
    // https://ja.wikipedia.org/wiki/%E5%B9%B3%E5%9D%87%E5%BE%8B
    // 1オクターブ (6音) で周波数が2倍になり、半音ごとに周波数は「2の1/12乗」倍になる
    public static double EqualTemperamentFrequency(double relativePositionInScale /* ベースとなる音 A4 から何音上か */)
        => A4Frequency * System.Math.Pow(2.0, relativePositionInScale * 2.0 / 12.0);
}

// 音符の長さ
enum Duration
{
    Whole     = 1600       , // 全音符
    Half      = Whole   / 2, // 二分音符
    Quarter   = Half    / 2, // 四分音符
    Eighth    = Quarter / 2, // 八分音符
    Sixteenth = Eighth  / 2, // 十六分音符
}

class Program
{
    const double relativePositionOfCSharp = -4.0; //  C♯(嬰ハ) は A(イ) より4音下

    static void Main()
    {
        //PlayCSharpNote();
        PlayCSharpEqualTemperament();

        Console.ReadKey();
    }

    //// 音名  C♯ の音を演奏 (Beep で)
    //static void PlayCSharpNote()
    //    => Beep(frequency: MusicalScale.EqualTemperamentFrequency(relativePositionOfCSharp), duration: Duration.Quarter);

    //  C♯ の音階を演奏 (平均律を Beep で)
    static void PlayCSharpEqualTemperament()
        => new[] { 0.0, 1.0, 2.0, 2.5, 3.5, 4.5, 5.5, 6.0 } // ドレミファソラシドの各音がドから何音離れているか
            .ForEach(note => Beep(frequency: MusicalScale.EqualTemperamentFrequency(note + relativePositionOfCSharp), duration: Duration.Quarter));

    // 指定された周波数/長さの音を鳴らす
    static void Beep(double frequency, Duration duration)
        => Console.Beep(frequency: (int)System.Math.Round(frequency), duration: (int)duration);
}
C# で C♯ の音階を純正率で演奏する (Beep)

純正率でもやってみよう。

純正率では各音が単純な整数比になるため、和音にしたときに平均律のようにわずかな周波数のずれからくるうなりが生ずることがなく、綺麗にハモれる。

詳細は省略するが、純正率では、ドレミファソラシドの周波数が、それぞれ、1/1、9/8、5/4、4/3、3/2、5/3、15/8、2/1 となる。

これを踏まえて、C# で書いてみる。

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

static class EnumerableExtentions
{
    public static void ForEach<T>(this IEnumerable<T> @this, Action<T> action)
    {
        foreach (var element in @this)
            action(element);
    }
}

static class MusicalScale
{
    public static double A4Frequency { get; set; } = 440.0; // ベースとなる音 A4 (デフォルトは440Hz)

    // 純正律の場合の各音 (ドレミファソラシド) の相対的な周波数の倍率
    readonly static double[] JustIntonationRelativeFrequencyScales = new[] {
            1.0 / 1.0, 9.0 / 8.0, 5.0 / 4.0, 4.0 / 3.0, 3.0 / 2.0, 5.0 / 3.0, 15.0 / 8.0, 2.0 / 1.0
        };

    // 平均律 (equal temperament) | Wikipedia
    // https://ja.wikipedia.org/wiki/%E5%B9%B3%E5%9D%87%E5%BE%8B
    // 1オクターブ (6音) で周波数が2倍になり、半音ごとに周波数は「2の1/12乗」倍になる
    public static double EqualTemperamentFrequency(double relativePositionInScale /* ベースとなる音 A4 から何音上か */)
        => A4Frequency * System.Math.Pow(2.0, relativePositionInScale * 2.0 / 12.0);

    // 純正律 (just intonation) - Wikipedia
    // https://ja.wikipedia.org/wiki/%E7%B4%94%E6%AD%A3%E5%BE%8B
    public static double JustIntonationFrequency(double keynoteRelativePositionInScale, int noteIndex /* ドレミファソラシドの各音のインデックス (0~7) */)
        => EqualTemperamentFrequency(keynoteRelativePositionInScale) * JustIntonationRelativeFrequencyScales[noteIndex];
}

// 音符の長さ
enum Duration
{
    Whole     = 1600       , // 全音符
    Half      = Whole   / 2, // 二分音符
    Quarter   = Half    / 2, // 四分音符
    Eighth    = Quarter / 2, // 八分音符
    Sixteenth = Eighth  / 2, // 十六分音符
}

class Program
{
    const double relativePositionOfCSharp = -4.0; //  C♯(嬰ハ) は A(イ) より4音下

    static void Main()
    {
        //PlayCSharpNote();
        //PlayCSharpEqualTemperament();
        PlayCSharpJustIntonation();

        Console.ReadKey();
    }

    //// 音名  C♯ の音を演奏 (Beep で)
    //static void PlayCSharpNote()
    //    => Beep(frequency: MusicalScale.EqualTemperamentFrequency(relativePositionOfCSharp), duration: Duration.Quarter);

    ////  C♯ の音階を演奏 (平均律を Beep で)
    //static void PlayCSharpEqualTemperament()
    //    => new[] { 0.0, 1.0, 2.0, 2.5, 3.5, 4.5, 5.5, 6.0 } // ドレミファソラシドの各音がドから何音離れているか
    //        .ForEach(note => Beep(frequency: MusicalScale.EqualTemperamentFrequency(note + relativePositionOfCSharp), duration: Duration.Quarter));

    //  C♯ の音階を演奏 (純正率を Beep で)
    static void PlayCSharpJustIntonation()
        => Enumerable.Range(start: 0, count: 8) // ドレミファソラシドの各音のインデックス (0~7)
                     .ForEach(noteIndex => Beep(frequency: MusicalScale.JustIntonationFrequency(relativePositionOfCSharp, noteIndex), duration: Duration.Quarter));

    // 指定された周波数/長さの音を鳴らす
    static void Beep(double frequency, Duration duration)
        => Console.Beep(frequency: (int)System.Math.Round(frequency), duration: (int)duration);
}
C# で C♯ を鳴らす (Microsoft.SmallBasic.Library.Sound.PlayMusic)

もう一つ試そう。 Small Basic の機能を使う方法だ。

そのために、まず Visual Studio から NuGet で "SmallBasicLib" をインストールする。

NuGet で "SmallBasicLib" をインストール
NuGet で "SmallBasicLib" をインストール

このライブラリーの機能の一つに、MML (Music Macro Language) を演奏するというものがある。 Microsoft.SmallBasic.Library.Sound.PlayMusic というメソッドを使うことでテキストで記述された旋律を演奏できる。

これを使って、平均律で C♯ の音階 (ドレミファソラシド) を演奏してみよう。 詳しい MML の記述方法は、下記ソースコードのコメントを見てほしい。

C# で C♯ の音階を平均律で演奏する (Microsoft.SmallBasic.Library.Sound.PlayMusic)
using Microsoft.SmallBasic.Library;
using System;

class Program
{
    static void Main()
    {
        PlayCSharpMusic();

        Console.ReadKey();
    }

    //  C♯ の音階を演奏 (平均率を Small Basic Library で)
    static void PlayCSharpMusic() =>
        //  C♯ (嬰ハ長調) | Wikipedia
        // https://ja.wikipedia.org/wiki/%E5%AC%B0%E3%83%8F%E9%95%B7%E8%AA%BF
        // シャープ7箇所(F, C, G, D, A, E, B)
        //  C♯ D♯ F F♯ G♯ A♯ C  C♯ (嬰ハ 嬰二 ヘ 嬰ヘ 嬰ト 嬰イ ハ 嬰ハ)
        Sound.PlayMusic(notes: "L4 O4 C+ D+ F F+ G+ A+ > C C+");

    // Sound.PlayMusic の notes
    //
    // C, D, E, F, G, A, B: 音名
    // R : 休符
    // C+: Cは音名、音を半音上げる (♯)
    // C-: Cは音名、音を半音下げる (♭)
    // Cn: n は音の長さ (1: 全音符、2: 二分音符、2.: 付点二分音符、4: 四分音符、4.: 付点四分音符、…)
    // Ln: n はデフォルトの音の長さ
    // On: n はオクターブ (デフォルトは4)
    // > : 1オクターブ上に
    // < : 1オクターブ下に
}
C# で C♯ の曲を演奏する (Microsoft.SmallBasic.Library.Sound.PlayMusic)

実は C♯、つまり嬰ハ長調の曲というのはそんなに多くないのだが、例えば、バッハ の「平均律クラヴィーア 第1巻 プレリュード 第3番 嬰ハ長調 BWV 848」や「前奏曲とフーガ 第3番 嬰ハ長調 BWV 872」などがそうだ。

例えば下記で聴くことができる (執筆時点)。

試しに、一部だけ C# で書いてみよう。 C♯ の響きを雰囲気だけでも味わっていただければ幸いだ。

using Microsoft.SmallBasic.Library;
using System;

class Program
{
    static void Main()
    {
        PlayBWV848();
        //PlayCSharpMusic();

        Console.ReadKey();
    }

    // バッハ 平均律クラヴィーア 第1巻 プレリュード 第3番 嬰ハ長調 BWV 848
    // 雰囲気だけ
    static void PlayBWV848() =>
        Sound.PlayMusic("L16 O5 E+ C+ > G+ < C+ E+ C+" +
                        "F+ C+ F+ C+ F+ C+" +
                        "G+ C+ G+ C+ G+ C+" +
                        "A+ C+ A+ C+ A+ C+" +
                        "G+ C+ G+ C+ G+ C+" +
                        "F+ E+ D+ E+ F+ D+" +
                        "E+ D+ C+ D+ E+ C+" +
                        "D+ E+ D+ C+ < B+ A+" +
                        "< B+ G+ D+ G+ B+ G+" +
                        "> C+ < G+ > C+ < G+ > C+ < G+" +
                        "> D+ < G+ > D+ < G+ > D+ < G+" +
                        "> E+ < G+ > E+ < G+ > E+ < G+" +
                        "> D+ < G+ > D+ < G+ > D+ < G+" +
                        "> C+ < B+ A+ B+ > C+ < A+" +
                        "B+ A+ G+ A+ B+ G+" +
                        "A+ B > > F+ E+ D+ E+" +
                        "F+ D+ < A+ > D+ F+ D+" +
                        "G+ D+ G+ D+ G+ D+" +
                        "A+ D+ A+ D+ A+ D+" +
                        "B D+ B D+ B D+" +
                        "A+ D+ A+ D+ A+ D+" +
                        "G+ F+ E+ F+ G+ E+" +
                        "F+ E+ D+ E+ F+ D+" +
                        "E+ F+ E+ D+ C+ < B+");

    ////  C♯ の音階を演奏 (平均率を Small Basic Library で)
    //static void PlayCSharpMusic() =>
    //    //  C♯ (嬰ハ長調) | Wikipedia
    //    // https://ja.wikipedia.org/wiki/%E5%AC%B0%E3%83%8F%E9%95%B7%E8%AA%BF
    //    // シャープ7箇所(F, C, G, D, A, E, B)
    //    //  C♯ D♯ F F♯ G♯ A♯ C  C♯ (嬰ハ 嬰二 ヘ 嬰ヘ 嬰ト 嬰イ ハ 嬰ハ)
    //    Sound.PlayMusic(notes: "L4 O4 C+ D+ F F+ G+ A+ > C C+");

    // Sound.PlayMusic の notes
    //
    // C, D, E, F, G, A, B: 音名
    // R : 休符
    // C+: Cは音名、音を半音上げる (♯)
    // C-: Cは音名、音を半音下げる (♭)
    // Cn: n は音の長さ (1: 全音符、2: 二分音符、2.: 付点二分音符、4: 四分音符、4.: 付点四分音符、…)
    // Ln: n はデフォルトの音の長さ
    // On: n はオクターブ (デフォルトは4)
    // > : 1オクターブ上に
    // < : 1オクターブ下に
}

2017年07月12日

[C#][.NET] Shos.CsvHelper (CSV を読み書きするためのシンプルなライブラリー)

CSV Icon

csv (Comma-Separated Values または Character-Separated Values) を読み書きするためのシンプルなライブラリーを書いてみた。

csv 形式のファイルは Excel などで表示/編集することができ、便利なので、そこそこ必要になる。 csv を読み書きするライブラリーは既に他にあるが、よりシンプルな使い勝手を目指してみた。

このライブラリーを使うと、プレーンなオブジェクトのコレクション (IEnumerable<Something>) を csv 形式で読み書きすることができる。

使い方

使い方の概要

まず、何か IEnumerable<TElement> なものを用意する:

    // 何か IEnumerable<TElement> なもの
    IEnumerable<ToDo> toDoes = new ToDoList();

例えばこんなクラス:

    class ToDoList : IEnumerable<ToDo> // サンプル コレクション
    {
        public IEnumerator<ToDo> GetEnumerator()
        {
            yield return new ToDo { Id = 1, Title = "filing tax returns", Deadline = new DateTime(2018, 12, 1) };
            yield return new ToDo { Id = 2, Title = "report of a business trip", Detail = "\"ASAP\"", DaySpan = new DaySpan(3), Priority = Priority.High };
            yield return new ToDo { Id = 3, Title = "expense slips", Detail = "book expenses: \"C# 6.0 and the .NET 4.6 Framework\",\"The C# Programming\"", Priority = Priority.Low, Done = true };
            yield return new ToDo { Id = 4, Title = " wish list ", Detail = " \t (1) \"milk\"\n \t (2) shampoo\n \t (3) tissue ", Priority = Priority.High };
        }

        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    }

これを、次のように書きこめる:

    const string csvFileName = "todo.csv";
    await toDoes.WriteCsvAsync(csvFileName);

出来上がった csv ファイルは次のようになる:

  • 値がカンマや区切り文字、改行(<CR>、<LF>)、ダブルクォーテーションを含んでいる場合は、ダブルクォーテーションで囲まれる
  • 値の中のダブルクォーテーションは、ダブルクォーテーション2つに置き換わる
Id,Title,Deadline,Done,Priority,Details,DaySpan
1,filing tax returns,2018/12/01 0:00:00,False,Middle,,0
2,report of a business trip,2017/07/12 13:13:01,False,High,"""ASAP""",3
3,expense slips,2017/07/12 13:13:01,True,Low,"book expenses: ""C# 6.0 and the .NET 4.6 Framework"",""The C# Programming""",0
4, wish list ,2017/07/12 13:13:01,False,High," 	 (1) ""milk""
 	 (2) shampoo
 	 (3) tissue ",0

作られた csv ファイルを Excel で 開いた様子
作られた csv ファイルを Excel で 開いた様子

読みこみは次のような感じ:

    IEnumerable<ToDo> newToDoes = await CsvSerializer.ReadCsvAsync<ToDo>(csvFileName);

読み書きするもの

コレクションの要素の "get" と "set" の両方を持つ public なプロパティが csv ファイルに読み書きされる。

書きこみ時

書きこみ時には、型を問わず、"ToString()" メソッドで文字列に変換される。

読みこみ時

読みこみ時には、文字列型はそのまま、enum (列挙型) も文字列の通りの値として読みこまれる。 それ以外の型のときは、"TryParse" か "Parse" を使って文字列を値に変更しようとする。 このいずれもできない型は読みこまれない。

"get" と "set" の両方を持っていて、かつ、次のいずれかの型であるプロパティが読みこまれる。

  • 文字列型
  • enum
  • デフォルト コンストラクターを持ち "TryParse" か "Parse" ができる型 (int などの基本的な数値型や DateTime、"TryParse" か "Parse" を持つユーザー定義型)
その他のルール
  • [CsvIgnore()] 属性が付けられたプロパティは、読み書きされない
  • [ColumnName("列名")] 属性が付けられたプロパティは、csv ファイルでの列名が指定されたものに変更される

例えば、上記 ToDo クラスはこんな型:

    // 要素の型のサンプル
    // 〇 の付いたプロパティ: 読み書きされる
    // × の付いたプロパティ: 読み書きされない
    class ToDo
    {
        public int      Id       { get; set; }                     // 〇
        public string   Title    { get; set; } = "";               // 〇
        public DateTime Deadline { get; set; } = DateTime.Now;     // 〇
        public bool     Done     { get; set; }                     // 〇
        public Priority Priority { get; set; } = Priority.Middle;  // 〇 ユーザー定義 enum 
        [ColumnName("Details")]
        public string   Detail   { get; set; } = "";               // 〇 [ColumnName("Details")] で csv ファイルでの列名が "Details" に変更される
        public DaySpan  DaySpan  { get; set; }                     // 〇 "Parse" を持つユーザー定義型 ("TryParse" は持たない)
        [CsvIgnore()]
        public string   Option   { get; set; } = "";               // × [CsvIgnore()] が付けられているので無視される
        public string   Version => "1.0";                          // × get しかできないプロパティなので無視される
    }

上記 ToDo クラスで使われているユーザー定義型は次の通り:

    // ユーザー定義の enum の例
    enum Priority { High, Middle, Low }

    // "Parse" を持つユーザー定義型 ("TryParse" は持たない) の例
    struct DaySpan
    {
        public int Value { get; private set; }

        public DaySpan(int value) => Value = value;
        public static DaySpan Parse(string text) => new DaySpan(int.Parse(text));
        public override string ToString() => Value.ToString();
    }

ヘッダーの有無

csv の読み書きは、ヘッダー部分 (例えば、上記 csv ファイルの一行目) がない場合でも可能になっている。

ただし、ヘッダー部分がある場合は、列が入れ替わっていてもヘッダー部を照合することで読みこみ可能だが、ヘッダー部分がない場合は、列を入れ替えると読みこむことができない。

ヘッダー部分無しでの書きこみの例:

    await toDoes.WriteCsvAsync(csvFilePathName: csvFileName, hasHeader: false);

出来上がった csv ファイル:

1,filing tax returns,2018/12/01 0:00:00,False,Middle,,0
2,report of a business trip,2017/07/06 18:08:13,False,High,"""ASAP""",3
3,expense slips,2017/07/06 18:08:13,True,Low,"book expenses: ""C# 6.0 and the .NET 4.6 Framework"",""The C# Programming""",0
4, wish list ,2017/07/12 13:13:01,False,High," 	 (1) ""milk""
 	 (2) shampoo
 	 (3) tissue ",0

作られた csv ファイルを Excel で 開いた様子
作られた csv ファイルを Excel で 開いた様子

こうしたヘッダー部分の無い csv ファイルを読みこむときは次のようにする:

    IEnumerable<ToDo> newToDoes = await CsvSerializer.ReadCsvAsync<ToDo>(csvFilePathName: csvFileName, hasHeader: false);    

その他の指定方法

文字コードは変更可能 (デフォルトは UTF8)。

    CsvSerializer.Encoding = Encoding.GetEncoding(0);

区切り文字も変更可能 (デフォルトは ',')。

    CsvSerializer.Separator = '\t';

ファイル名の代わりに stream も指定できる。

    using (var stream = new FileStream(csvFileName, FileMode.Create))
        await collection.WriteCsvAsync(stream);

非同期メソッド以外に同期メソッドもある。

    toDoes.WriteCsv(csvFileName);

NuGet と GitHub

各ライブラリーは NuGet に公開されているので、Visual Studio からインストールできる。

ソースコードは次の場所で公開:

含まれるプロジェクトは次の通り:

Shos.CsvHelper

  • Csv ライブラリー
  • .NET Standard ライブラリー版
  • .NET Standard 1.3 以降でビルドできる
  • .NET Network 4.6 以降、または .NET Core 1.1 以降対象
  • NuGet パッケージとしてインストールできる: NuGet Gallery | Shos.CsvHelper

Shos.CsvHelper.NetFramework

Shos.CsvHelperSample.NetCore

  • Shos.CsvHelper を利用する .NET Core コンソール アプリケーションのサンプル

Shos.CsvHelperSample.NetFramework

  • Shos.CsvHelper.NetFramework を利用する .NET Framework コンソール アプリケーションのサンプル

2017年07月01日

Microsoft MVP を再受賞しました (My 13th MVP Award from Microsoft)

MVP_Logo_Horizontal_Preferred_Cyan300_CMYK_72ppi.png

Microsoft MVP Award を再受賞しました。13年目になります。

My MVP Profile

  • Microsoft MVP for Development Tools - Visual C# (Jul. 2005 - Dec. 2014)
  • Microsoft MVP for .NET (Jan. 2015 - Oct. 2015)
  • Microsoft MVP for Visual Studio and Development Technologies (Nov. 2015 - Jun. 2018)

素晴らしい機会とこの機会によって関われた皆様に感謝です。

2016年12月23日

[C#] Tips: interface と partial class で横断的関心事を分離

C丼

C# Advent Calendar 2016 の12月23日の記事。

以前、「C# Tips: interface を 抽象クラス (abstract class) とどう使い分けるか」という記事を書いた。 その中で、「アスペクトの実装を便宜上 (言語の都合上) interface で行う」というイディオムについて触れた。 この記事はその続きだ。 より具体的にこのイディオムを紹介する。

分割攻略と疎結合/高凝集

ソフトウェア開発というものは往々にして複雑さとの戦いになるものだが、プログラムの設計において複雑さに立ち向かうための基礎となる考え方に、分割攻略 (Divide and Conquer、分割統治) というものがある。 大きく複雑な問題をそのまま解くのは大変なので、より小さくシンプルな問題に分けることで、全体としての複雑さを下げて解こう、という戦略だ。

大きく複雑な問題をそのまま解く
大きく複雑な問題をそのまま解く

上の図のようにすると大変なので、たとえば次のようにする。 10倍の大きさの問題の複雑さは10倍ではきかない、というのが基本的なアイディアだ。

分割攻略: 大きく複雑な問題を複数のより小さくシンプルな問題に分けて解く
分割攻略: 大きく複雑な問題を複数のより小さくシンプルな問題に分けて解く

分割するときに重要となるのが、「どう分けるか」だ。 一般的には次のようになれば良いとされている。

  • 疎結合 (low coupling): 分割されたもの同士の結び付きを少なく/弱く・
  • 高凝集 (high cohesion): 一つの分割単位の中が一つの関心事だけになり、かつ、その関心事がその分割単位の中だけにある

ソフトウェアの設計では、疎結合で高凝集になるように分割することが大切、ということだ。 いくら小さな単位に分けても、問題が互いにからみあっていたり、シンプルな単位に分かれていなかったりすると、うまく複雑さが減ってくれないからだ。

そして、これを実現するために、様々な設計の考え方がある。 オブジェクト指向という考え方を使っても、ある程度これを行うことができる。 オブジェクト指向では、クラスやオブジェクトなどという単位に分けることでこれを行うわけだ。

オブジェクト指向と横断的関心事

最初の CAD の設計

ところが、オブジェクト指向といっても万能なわけではなく、クラスやオブジェクトなどに分けるというだけでは、必ずしも高凝集にならない。

例をみてみよう。 以下は、C# による CAD (Computer-Aided Design) の設計の例だ。

CAD のクラス図の例
CAD のクラス図の例

図の中の赤いクラスがモデル (Model) で、抽象図形クラス Figure と、そこから継承した複数の具象図形クラス LineFigure・EllipseFigure がある。

青いクラスがビュー (View) で、モデルの IEnumerable<Figure> というインタフェイスを使ってモデルとデータバインドし、個々の図形を描画する。

C# によるソース コードは、たとえば次のようなものになる (説明に必要のない部分は省略)。

MiniCad.cs
using System;
using System.Collections;
using System.Collections.Generic;

abstract class Figure
{
    public abstract void Draw();
}

class LineFigure : Figure
{
    public override void Draw() => Console.WriteLine("Line!"); // 仮実装
}

class EllipseFigure : Figure
{
    public override void Draw() => Console.WriteLine("Ellipse!"); // 仮実装
}

class CadModel : IEnumerable<Figure>
{
    public IEnumerator<Figure> GetEnumerator()
    {
        yield return new LineFigure   ();
        yield return new LineFigure   ();
        yield return new EllipseFigure();
        yield return new LineFigure   ();
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

class CadView
{
    public IEnumerable<Figure> DataSource {
        set { value.ForEach(figure => figure.Draw()); }
    }
}

class Program
{
    static void Main()
    {
        new CadView().DataSource = new CadModel();
    }
}

static class EnumerableExtensions
{
    public static void ForEach<TElement>(this IEnumerable<TElement> @this, Action<TElement> action)
    {
        foreach (var element in @this)
            action(element);
    }
}

この実装では仮にコンソールに文字列を出力しているだけなので、実行結果は次のようになる。

Line!
Line!
Ellipse!
Line!

この段階ではまだ単純な設計であるため、オブジェクト指向を使うことでそこそこ高凝集になっている。

CAD の設計の変更

さて、ここまで作った後で、ファイルなどに保存するときのためにシリアライズ機能を付けたくなったとする。 CAD は一般的に様々なフォーマットをサポートすることが多いものだが、ここでは仮に SVG (Scalable Vector Graphics) 形式と独自のバイナリ―形式をサポートするものとしよう。

このために、それぞれの図形クラスに、2つの形式のシリアライズのための SvgSerialize と BinarySerialize という2つのメソッドを追加してみるとどうなるだろうか。

シリアライズ機能を付けた CAD のクラス図の例
シリアライズ機能を付けた CAD のクラス図の例

C# によるソース コードは、たとえば次のように変わるだろう。

MiniCad.cs
using System;
using System.Collections;
using System.Collections.Generic;

abstract class Figure
{
    public abstract void Draw();
    public abstract void SvgSerialize();
    public abstract void BinarySerialize();
}

class LineFigure : Figure
{
    public override void Draw() => Console.WriteLine("Line!"); // 仮実装
    public override void SvgSerialize() => Console.WriteLine("SvgSerialize line!"); // 仮実装
    public override void BinarySerialize() => Console.WriteLine("BinarySerialize line!"); // 仮実装
}

class EllipseFigure : Figure
{
    public override void Draw() => Console.WriteLine("Ellipse!"); // 仮実装
    public override void SvgSerialize() => Console.WriteLine("SvgSerialize ellipse!"); // 仮実装
    public override void BinarySerialize() => Console.WriteLine("BinarySerialize ellipse!"); // 仮実装
}

class CadModel : IEnumerable<Figure>
{
    public void SvgSerialize() => this.ForEach(figure => figure.SvgSerialize());
    public void BinarySerialize() => this.ForEach(figure => figure.BinarySerialize());

    public IEnumerator<Figure> GetEnumerator()
    {
        yield return new LineFigure   ();
        yield return new LineFigure   ();
        yield return new EllipseFigure();
        yield return new LineFigure   ();
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

class CadView
{
    public IEnumerable<Figure> DataSource {
        set { value.ForEach(figure => figure.Draw()); }
    }
}

class Program
{
    static void Main()
    {
        var model = new CadModel();
        new CadView().DataSource = model;
        model.SvgSerialize   ();
        model.BinarySerialize();
    }
}

static class EnumerableExtensions
{
    public static void ForEach<TElement>(this IEnumerable<TElement> @this, Action<TElement> action)
    {
        foreach (var element in @this)
            action(element);
    }
}

そして、これが実行結果だ。

Line!
Line!
Ellipse!
Line!
SvgSerialize line!
SvgSerialize line!
SvgSerialize ellipse!
SvgSerialize line!
BinarySerialize line!
BinarySerialize line!
BinarySerialize ellipse!
BinarySerialize line!

しかし、この設計では、高凝集になっていない部分がでてきてしまっているのがお分かりだろうか。

図形クラスという部分に関しては、ある程度高凝集になっている。 各図形に関する仕事 (それぞれの図形の描画やシリアライズ) は、ちゃんと各図形でやっているように見える。

ところが、SVG シリアライズ、あるいはバイナリー シリアライズという関心事に目を向けてみると、そちらはどうなっているだろう。

たとえば、SVG シリアライズに関するメソッドは Figure、LineFigure、EllipseFigure、CadModel という複数のクラスにまたがってしまっている。 バイナリー シリアライズについても同様だ。 これでは、高凝集、すなわち「一つの分割単位の中が一つの関心事だけになり、かつ、その関心事がその分割単位の中だけにある」とは言えないだろう。

このように、オブジェクト指向では複数のクラスをまたがる関心事というものがでてきて設計に困ることがある。 この場合だと、それぞれの図形への関心事とシリアライズという関心事が直交してしまっている。 そのため、一方の視点での分割によるかたまりが、他方の視点での分割をまたがってしてしまうのだ。 このような関心事は、横断的関心事 (crosscutting concern) と呼ばれる。

この例では、はじめは図形のクラス設計を行っていて、後からSVG シリアライズやバイナリー シリアライズといった横断的関心事がでてきてしまったのだ。

interface と partial class で横断的関心事を分離

なんとか、それまでのクラスの設計をこわすことなく、この横断的関心事を分離できないものだろうか。

この記事では、C# の interface と partial class を使ったイディオムをご紹介したい。

先のコードのようにいきなりシリアライズが必要なそれぞれのクラスの中に SvgSerialize メソッドと BinarySerialize メソッドを追加するのではなく、先ず partial class とだけしてみる。

MiniCad.cs
using System;
using System.Collections;
using System.Collections.Generic;

abstract partial class Figure
{
    public abstract void Draw();
}

partial class LineFigure : Figure
{
    public override void Draw() => Console.WriteLine("Line!"); // 仮実装
}

partial class EllipseFigure : Figure
{
    public override void Draw() => Console.WriteLine("Ellipse!"); // 仮実装
}

partial class CadModel : IEnumerable<Figure>
{
    public IEnumerator<Figure> GetEnumerator()
    {
        yield return new LineFigure   ();
        yield return new LineFigure   ();
        yield return new EllipseFigure();
        yield return new LineFigure   ();
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

class CadView
{
    public IEnumerable<Figure> DataSource {
        set { value.ForEach(figure => figure.Draw()); }
    }
}

class Program
{
    static void Main()
    {
        var model = new CadModel();
        new CadView().DataSource = model;
        model.SvgSerialize   ();
        model.BinarySerialize();
    }
}

static class EnumerableExtensions
{
    public static void ForEach<TElement>(this IEnumerable<TElement> @this, Action<TElement> action)
    {
        foreach (var element in @this)
            action(element);
    }
}

この Main メソッドでは、SvgSerialize メソッドを BinarySerialize メソッドを使っているが、この時点ではどこにも実装がない。

次に、SvgSerialize メソッドを持つという interface である ISvgSerializable を作り、それを各モデル クラスで実装する。そして、 この実装を partial class の機能を用いて別ファイルで行うことにする。 こんな具合だ。

ISvgSerializable.cs
using System;

interface ISvgSerializable
{
    void SvgSerialize();
}

partial class Figure : ISvgSerializable
{
    public abstract void SvgSerialize();
}

partial class LineFigure
{
    public override void SvgSerialize() => Console.WriteLine("SvgSerialize line!"); // 仮実装
}

partial class EllipseFigure
{
    public override void SvgSerialize() => Console.WriteLine("SvgSerialize ellipse!"); // 仮実装
}

partial class CadModel : ISvgSerializable
{
    public void SvgSerialize() => this.ForEach(figure => figure.SvgSerialize());
}

こうすると、SVG シリアライズという責務を記述するコードがすべてこのファイルに集まることになる。

BinarySerialize メソッドの方も、同様に別ファイルを用意する。 そこで IBinarySerializable という interface を作り、それを各モデル クラスで実装する。

IBinarySerializable.cs
using System;

interface IBinarySerializable
{
    void BinarySerialize();
}

partial class Figure : IBinarySerializable
{
    public abstract void BinarySerialize();
}

partial class LineFigure
{
    public override void BinarySerialize() => Console.WriteLine("BinarySerialize line!"); // 仮実装
}

partial class EllipseFigure
{
    public override void BinarySerialize() => Console.WriteLine("BinarySerialize ellipse!"); // 仮実装
}

partial class CadModel : IBinarySerializable
{
    public void BinarySerialize() => this.ForEach(figure => figure.BinarySerialize());
}

実行結果は変わらない。 クラス図も ISvgSerializable と IBinarySerializable という2つのインタフェイスが加わっただけだ。

インタフェイス追加後の CAD のクラス図の例
インタフェイス追加後の CAD のクラス図の例

これは、オブジェクト指向で横断的関心事を分離したわけではない。 オブジェクト指向にも限界がある。 ここでは、横断的関心事をクラスにマッピングすることではうまく分離できなかった。

そのため、ここでは C# の機能を使い、それまでの関心事に直交した新たな関心事をファイルにマッピングすることで分離した、ということだ。

2016年12月16日

Visual Studio 2017 のライブ ユニット テスト機能による「車窓からの TDD」

Visual Studio Advent Calendar 2016 の12月16日の記事。

logo

コーディング機能やデバッグやテストの機能が大幅パワーアップ! 次期バージョン「Visual Studio 2017 RC」 | CodeZine という記事を書かせていただき、その中で、「Visual Studio 2017 RC」の「特におすすめの新機能」としてライブ ユニット テストに触れた。 もう少し具体的にライブ ユニット テストを紹介してみたい。

題材として、『ソフトウェア技術者サミット in 福井 2016』 (7月16日) の「テスト駆動開発ライブ 〜C#ペアプログラミング実況中継」というセッションの中で、平鍋健児氏と C# と Visual Studio 2015 を使っておこなったテスト駆動開発 (Test-Diven Development: TDD) のデモンストレーションを用いることにする。

「テスト駆動開発ライブ 〜C#ペアプログラミング実況中継」 | 『ソフトウェア技術者サミット in 福井 2016』
「テスト駆動開発ライブ 〜C#ペアプログラミング実況中継」 | 『ソフトウェア技術者サミット in 福井 2016』

このデモンストレーションは、北野弘治氏と平鍋健児氏による「車窓からの TDD | オブジェクト倶楽部」 を C# で再現したものだ。 テストを書きながら、スタックのクラスを作成していく。 順を追ってやってみよう。

プロジェクトの作成

最初に、Visual Studio 2017 RC を起動し、テストするプロジェクトとテストされるプロジェクトを作る。

  • テストするプロジェクトの作成
    • メニューから「ファイル > 新規作成 > プロジェクト > Visual C# > テスト > 単体テスト プロジェクト Visual C#」
単体テスト プロジェクト (C#) の作成
テストするプロジェクト – 単体テスト プロジェクト (C#) の作成
  • テストされるプロジェクトの作成
    • ソリューション エクスプローラでソリューション名を右クリックし、「追加 > 新しいプロジェクト > Visual C# > クラス ライブラリ (.NET Framework) Visual C#」
テストされるプロジェクト – クラス ライブラリ (C#) の作成

ライブ ユニット テストの開始

では、早速 Visual Studio 2017 の新機能であるライブ ユニット テストを開始しよう。 次のようにメニューから選ぶだけだ。

  • メニュー > テスト > Live Unit Testing > Start
ライブ ユニット テストの開始
ライブ ユニット テストの開始

最初のテスト

それでは、単体テスト プロジェクトに最初のテストを追加してみよう。 TDD では、テストされるコードよりテスト コードを先に書くことが多い。

最初のテスト: 「作ったら空」

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Shos.Collections.Test
{
    [TestClass]
    public class Stackのテスト
    {
        [TestMethod]
        public void 作ったら空()
        {
            var stack = new Stack<int>();
            Assert.IsTrue(stack.IsEmpty);
        }
    }
}

クラス名を「Stackのテスト」とし、「作ったら空」というテストを追加した。 まだ Stack クラスは作っていないので、静的なエラー (コンパイル時エラー) になり、エラー箇所に赤い波線が表示される。

最初のテストを作った直後
最初のテストを作った直後

バルブ アイコンをクリックするか、エラー箇所にカーソルがある状態で 'Ctrl+.' をタイプし、クラス ライブラリ プロジェクトの方に Stack クラスを作成しよう。

Stack クラスの作成: 新しい型の生成
Stack クラスの作成: 新しい型の生成
Stack クラスの作成: クラス ライブラリ プロジェクトの方に Stack クラスを生成
Stack クラスの作成: クラス ライブラリ プロジェクトの方に Stack クラスを生成

OK ボタンを押すと、クラス ライブラリ プロジェクトに Stack クラスが生成される。

生成された Stack クラス

namespace Shos.Collections
{
    public class Stack<T>
    {
        public Stack()
        {
        }
    }
}

テストの方では、まだ "stack.IsEmpty" の部分が静的エラーになっている。

バブル アイコンまたは 'Ctrl+.' から「プロパティ 'Stack.IsEmpty' を生成します」を選ぼう。

プロパティ 'IsEmpty' の生成
プロパティ 'Stack.IsEmpty' の生成

Stack クラスに、プロパティ "IsEmpty" が生成される。 このように、Visual Studio の助けを借りることで、スムーズに TDD を行うことができる。

ライブ ユニット テスト

"IsEmpty" が作られたことで、で静的エラーが消えた。

すると、ここでライブ ユニット テスト機能が自動で働く。 テストする側のクラス「Stackのテスト」の左に、テストが失敗したことを示す赤い×印が表示される。

ライブ ユニット テストの結果: テストする側のクラス
ライブ ユニット テストの結果: テストする側のクラス

そして、テストされる側のクラス Stack の左にも、テストが失敗したことを示す赤い×印があらわれる。

ライブ ユニット テストの結果: テストされる側のクラス
ライブ ユニット テストの結果: テストされる側のクラス

では、テストを成功させるために、Stack クラスを修正しよう。 テスト コード中の "IsEmpty" にカーソルがある状態で 'F12' を押す (または、"IsEmpty" を右クリックして「定義に移動」)。

ここでは、"IsEmpty" の実装を "public bool IsEmpty => true;" に変更してみる。

そうすると、ライブ ユニット テストによって自動でテストが通り、Stack クラスと「Stackのテスト」クラスに表示されていた赤い×印緑のチェックに変わる。

ライブ ユニット テストの結果 - テスト成功 - Stack クラス
ライブ ユニット テストの結果 – テスト成功 – Stack クラス
ライブ ユニット テストの結果 - テスト成功 「Stackのテスト」クラス
ライブ ユニット テストの結果 – テスト成功 「Stackのテスト」クラス

このように TDD では、次のようなステップで踏み、それを繰り返しながら、プログラミングを進めていく。

  1. テストを書く
  2. (コンパイラーによる静的エラー – 静的なテスト)
  3. テストされる側のコードを書き、テストを失敗させる (ライブ ユニット テストによる動的エラー – 動的なテスト – レッド)
  4. テストを成功させる分だけテストされる側のコードを修正し、テストを成功させる (グリーン)
  5. 1 に戻る

テストされる側にコードを追加する場合は、テストを追加し一度テストを失敗させる。 そして、テストが通る分だけのコードをテストされる側に追加する。

テストによってコードのその時点でのあるべき姿を記述する。 つまり、メソッドなりクラスなりの外からみた仕様をテストで先に記述するわけだ。 このテストが通ったことをもって、コードがあるべき姿になったことにする。 これを繰り返すことで、あるべき姿を少しずつインクリメンタルに作っていくのだ。 メソッドやクラスが、その時点でのあるべき姿になったかどうかが、テストによってフィードバックされる。

そして、Visual Studio 2017 のライブ ユニット テスト機能を使えば、このフィードバックがリアルタイムに行われることになるのだ。 仕様を満たしていないコードがレッドで、仕様を満たしたコードがグリーンで、リアルタイムで可視化される。 とても効率の良いコーディング環境だ。

テスト「Pushしたら空じゃなくなる」の追加

さて、テストを追加してみよう。

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Shos.Collections.Test
{
    [TestClass]
    public class Stackのテスト
    {
        Stack<int> stack;

        [TestInitialize]
        public void 準備() => stack = new Stack<int>();

        [TestMethod]
        public void 作ったら空()
        {
            Assert.IsTrue(stack.IsEmpty);
        }

        [TestMethod]
        public void Pushしたら空じゃなくなる()
        {
            stack.Push(1);
            Assert.IsFalse(stack.IsEmpty);
        }
    }
}

"stack = new Stack<int>()" のコードが重複するのが嫌なので、"[TestInitialize] public void 準備() => stack = new Stack<int>();" というコードを追加した。 このメソッドは、個々のテストの実行前に一回ずつ呼ばれる。 この部分は、まだテストされていないが、このようなコードの左には、青い横棒が表示される。 テストしていないコードが可視化されるわけだ。 これにより、常にカバレッジが確認できる。

テストされていないコードの左には青い横棒
テストされていないコードの左には青い横棒

テストを追加した直後は、"stack.Push(1)" の "Push" が静的エラーになる。 Stack.Push メソッドがまだないためだ。 バブル アイコンまたは 'Ctrl+.' から「メソッド 'Stack.Push' を生成します」を選ぼう。 また動的テストが自動で走り、ちゃんと失敗する。

テスト「Pushしたら空じゃなくなる」の失敗
テスト「Pushしたら空じゃなくなる」の失敗

テストが通るように Stack クラスの Push メソッドを修正しよう。 テスト コード中の "Push" にカーソルがある状態で 'F12' を押す (または、"Push" を右クリックして「定義に移動」)。 "public void Push(int v) { throw new NotImplementedException(); }" の左には赤い×印が表示されているはずだ。

Stack クラスを、例えば次のように変更すると、テストが通って、緑のチェックに変わる。

Stack クラスを修正するとテストが通った
Stack クラスを修正するとテストが通った

TDD のリズム

ではまた、テストを追加しよう。 静的エラーになる (赤い波線)。

テスト「PushしてTopをみたらPushしたやつ」の追加
テスト「PushしてTopをみたらPushしたやつ」の追加

'Ctrl+.' から「プロパティ 'Stack.Top' を生成します」を選択。 また動的テストが自動で走り、ちゃんと失敗 (赤い×印)。

テスト「PushしてTopをみたらPushしたやつ」がちゃんと失敗
テスト「PushしてTopをみたらPushしたやつ」がちゃんと失敗

'F12' を押して、Stack クラスを修正。 また動的テストが自動で走って成功 (緑のチェック)。

Stack クラスを修正するとテストが成功 - Stack クラス
Stack クラスを修正するとテストが成功 – Stack クラス
Stack クラスを修正するとテストが成功 - 「Stackのテスト」クラス
Stack クラスを修正するとテストが成功 – 「Stackのテスト」クラス

今度は、テスト「PushせずにTopをみたら例外」の追加。失敗。

テスト「PushせずにTopをみたら例外」を追加すると失敗
テスト「PushせずにTopをみたら例外」を追加すると失敗

Stack クラスを修正するとテストが成功。

Stack クラスを修正するとテストが成功 - Stack クラス
Stack クラスを修正するとテストが成功 – Stack クラス
Stack クラスを修正するとテストが成功 - 「Stackのテスト」クラス
Stack クラスを修正するとテストが成功 – 「Stackのテスト」クラス

TDD ではレッドグリーンの繰り返しのリズムが大切だと思う。 テストのための無駄な作業を省いてくれるライブ ユニット テスト機能が、このリズムを心地よいものにし、プログラミングをより楽しくしてくれることだろう。

この例では、なるべく小さなステップで進めるようにしているので、テストが通る最小限の修正にとどめているが、コードが明らかであるような場合は、もっと大きな修正をしても構わないだろう。

リファクタリングについて

今回は出てきていないが、TDD では、テストが通った後にリファクタリング (外から見た仕様を変えずに内部構造を改善すること) を行うことが多い。Visual Studio 2017 のライブ ユニット テスト機能を使うと、このリファクタリングの際も常に自動で静的テストと動的テストによって、仕様どおりであり続けていることがチェックされることになる。 リファクタリング中に誤ってバグを作ってしまってもすぐに気付けることだろう。

リファクタリングでは、なるべくエラーが起きている時間を短くすることが肝要だが、こうしたエラーの可視化によるフィードバックが常に起こることは、エラー時間の最小化につながるだろう。

また、リファクタリング中の継続的なフィードバックによって、なるべくレッドにならないような手順でリファクタリングをするような良い習慣が生まれるのではないだろうか。

もちろん、Visual Studio 2017 で追加された多くの新たなリファクタリング機能も安全なリファクタリングに役立ってくれることだろう。

まとめ

Visual Studio 2017 RC の新たな機能であるライブ ユニット テストは、機能を使うことに手間を取られない。 機能を使うことに振り回されて、コーディングのリズムを狂わされることがない。 つまり、Visual Studio 2017 RC を使うと TDD が楽しく快適になる。

2016年07月02日

Microsoft MVP を再受賞しました (My 12th MVP Award from Microsoft)

MVP_Logo_Horizontal_Preferred_Cyan300_CMYK_72ppi.png

Microsoft MVP Award を再受賞しました。12年目になります。

My MVP Profile

  • Microsoft MVP for Development Tools - Visual C# (Jul. 2005 - Dec. 2014)
  • Microsoft MVP for .NET (Jan. 2015 - Oct. 2015)
  • Microsoft MVP for Visual Studio and Development Technologies (Nov. 2015 - Jun. 2017)

いくつになっても、こうして褒められるのは嬉しいものです。 マイクロソフトのような大企業が本気で褒めてくれる。ありがたいことです。

沢山の素晴らしい方々と出会える機会をいただいております。 お世話になっている皆様に感謝です。いつもありがとうございます。

2016年06月08日

[Event] 『ソフトウェア技術者サミット in 福井 2016』 (2016年7月16日)が開催されます

『ソフトウェア技術者サミット in 福井 2016』のロゴ

ソフトウェア技術者サミット in 福井 2016

~ITアーキテクトの第一人者に聞く! 福井で一流のエンジニアになる方法~

日本を代表するITアーキテクトの皆さんに、福井で語っていただきます!
一緒に福井で一流のソフトウェア エンジニアになる方法を学びましょう。
学生さん、新人さん、大歓迎です。

どうぞご参加ください。

■ 日時:

2016-07-16(土)13:00 - 18:30

■ 会場:

福井大学 文京キャンパス
総合研究棟VII (工学3号館 / 情報・メディア工学科) 311L講義室
福井県福井市文京3丁目9番1号

■ 主催/協力:

■ お申込み:

https://fitea.doorkeeper.jp/events/45122

ソフトウェア技術者サミット in 福井 2016

■ 参考サイト:

■ イベント チラシ:

『ソフトウェア技術者サミット in 福井 2016』のチラシ
 『ソフトウェア技術者サミット in 福井 2016』のチラシ

2016年04月01日

Build 2016 2日目の発表内容まとめ

※ 「Build 2016 1日目の発表内容まとめ」の続き。

2日目のキーノートに関する記事。

2日目の発表内容は以下の通りだ。

1. Xamarin が Visual Studio に

マイクロソフトが買収した Xamarin (C# を使って iOS や Android などのネイティブ アプリが作れるツール) が、Visual Studio に含まれることになった。

Visual Studio の Enterprise エディションや Professional エディションの他、無償版の Community エディションでも追加料金なしで使うことができるようになる。

2. Microsoft Azure 関連

3. その他

2016年03月31日

Build 2016 1日目の発表内容まとめ

マイクロソフトの最大の開発者向けイベント「Build 2016」がサンフランシスコで開催中。

Build 2016

内容は、一部ライブで観ることができる。

1日目に関する記事が、既にあちこちに上がっている。

1日目のキーノートに関する記事も沢山。

1日目の発表内容は以下の通りだ。

1. Windows 10 関連の発表

1.1. BashシェルをWindowsに搭載

Windows 10 で Linux のシェルのバイナリ―をそのまま動作できるようにするとのこと。

マイクロソフトは、最近 Windows 以外の OS との親和性をどんどんと高めており、その一環だろう。

1.2. Windows 10 の大型アップデートについて

2. 「Microsoft Bot Framework」を公開

クラウド上の「マシン ラーニング」機能の応用としての「人工知能」関連だ。

最近マイクロソフトや Google、Amazon などが取り組んでいることで話題になっている。

今回発表されたのは、Bot を作るためのフレームワーク。

3. Visual Studio 関連の発表

Visual Studio "15" Preview と Visual Studio 2015 Update 2 RTM が公開された。

Visual Studio 2015 Update 2 は「プログラムと機能」から Visual Studio 2015 の「変更」を選んでインストールするのが簡単だ。

また、MSDN からは Update 2  込みの Visual Studio 2015 がダウンロードできるようになっている。

4. その他

その他、以下のような発表があった。

4.1 Desktop App Converter

Win32/.NETアプリを UWP (*1) アプリに変換するツール。

(*1) UWP とは: ユニバーサル Windows プラットフォームの略。Windows 10 を積んだPC や Phone、XBox One、HoloLens、Surface Hub などで動作可能。

4.2 XBox Dev Mode

Visual Studio で XBox One 用に UWP アプリを開発可能に。Visual Studio  からリモートデバッグもできる。

4.3 HoloLens 関連

開発者エディションを提供開始。

4.4 Cortana 関連

様々なアップデートがあったようだ。

4.5 .NET Framework 4.6.2 Preview

執筆・講演のご依頼は 執筆・講演のご依頼 まで

2018年02月
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28      

アーカイブ

にほんブログ村

にほんブログ村 IT技術ブログへ
Powered by
Movable Type 3.35