Effective C# 3rd 読書メモ 47 アプリケーション固有の例外クラスを作る

例外は、発生した場所から遠く離れた場所で処理されることがある。例外オブジェクトには、エラーの発生原因に関する情報が全て含まれていなければならない。アプリケーション固有の例外を作りたくなるかもしれないが、そのためには様々な事項を考慮する必要がある。

はじめに、新しい例外を作る場合とその理由、作成方法について理解する必要がある。独自の例外クラスは、あなたのライブラリの利用者が、その例外をキャッチして特別な処理を行いたいと考えられる場合にのみ追加すべきである(たとえば、引数がnullの場合に例外を投げるだけなら、ArgumentExceptionで十分かもしれない)。

例外クラスの名前の末尾は「Exception」でなければならない。System.Exceptionか、あるいはそれ以外の適切な例外クラスを継承すべきである。また、ベースクラスの全てのコンストラクタを引き継ぐべきである。System.Exceptionをベースにした例外クラスは以下のように定義できる。

[Serializable]
public class MyException : Exception
{
    // デフォルトコンストラクタ
    public MyException() : base()
    {
    }

    // メッセージ付き
    public MyException(string s) : base(s)
    {
    }

    // メッセージと内部例外
    public MyException(string s, Exception e) : base(s, e)
    {
    }

    // 入力ストリームから作成
    public MyException(SerializationInfo i, StreamingContext c) : base(i, c)
    {
    }
}

モダンJavaScriptの練習に:『Electronではじめるアプリ開発』

Electronは、Webの技術(HTML+CSS+JavaScript)を使用してデスクトップアプリケーションを構築するためのフレームワークです。Atom、Visual Studio Codeといったモダンなエディタや、Slackのデスクトップ版などに利用されています。ChromiumというChromeのベースとなっているブラウザを使用することで、Windows/macOS/Linuxといった様々なOS上で動作する、クロスプラットフォームなデスクトップアプリケーションを構築することができます。

本書では、ES2015 + Reactという組み合わせでアプリケーションを構築していきます。ES2015やReactについての詳しい解説は無いので、あらかじめES2015のクラス構文やアロー関数式、Reactの基本的な使い方を身に着けておいたほうが読みやすいでしょう。

ES2015については『改訂新版JavaScript本格入門』がおすすめです。Reactについては、公式チュートリアルをやっておけば大丈夫でしょう。
以下のようなソースを見て、だいたい何をやっているかわかればOKです。

// Previewer.jsx
import React from "react";
import marked from "marked";
import style from "./Previewer.css";
import emojione from "emojione";

marked.setOptions({sanitize: true});

const renderer = new marked.Renderer();
renderer.text = (text) => {
    return emojione.shortnameToImage(text);
};

export default function Previewer(props) {
    return (
        <div
            id="previewer"
            className={`${props.className} ${style.previewer}`}
        >
            <span
                dangerouslySetInnerHTML={{__html: marked(props.value, {renderer})}}
            />
        </div>
    );
}

本書には、様々なライブラリを活用して、手早く高機能なアプリケーションを構築する方法が記載されています。また、各章はチュートリアル形式になっているので、実際に手を動かしながら学ぶことができます。Electronに興味がある人だけでなく、モダンなJavaScriptアプリケーションの組み方について理解を深めたい人にもおすすめできる内容です。

Effective C# 3rd 読書メモ 46 リソースの後始末にusingとtry/finallyを利用する

.NET Frameworkの管理下にない(アンマネージド)リソースを使用する型は、Dispose()メソッドによって明示的に解放すべきである。Dispose()メソッドをもつ型を使うときに、Dispose()を実行するのは あなたの 責任である。Dispose()が必ず呼ばれるようにする最良の方法は、using文を使うか、try/finallyブロックを使うかである。

public void ExecuteCommand(string connectionString,
    string command)
{
    using (var connection = new SqlConnection(connectionString))
    using (var sqlCommand = new SqlCommand(command, connection))
    {
        connection.Open();
        sqlCommand.ExecuteNonQuery();
    }
}

同様の処理をtry/finallyで書くと以下のようになる。

public void ExecuteCommand(string connectionString,
    string command)
{
    SqlConnection myConnection = null;
    SqlCommand myCommand = null;

    try
    {
        myConnection = new SqlConnection(connectionString);
        myCommand = new SqlCommand(command, myConnection);

        myConnection.Open();
        myCommand.ExecuteNonQuery();
    }
    finally
    {
        myConnection?.Dispose();
        myCommand?.Dispose();
    }
}

usingはコンパイル時の型がIDisposableを実装している場合にのみ利用可能で、任意の型に対して利用できるわけではない。

以下のように、usingをキャストと共に使うと、(1) objがIDisposableを実装している場合は、using文が実行される (2) objがIDisposableを実装していない場合は、using文は実行されない という結果になる。

object obj = Factory.CreateResource();
using (obj as IDisposable)
    WriteLine(obj.ToString());

Effective C# 3rd 読書メモ 45 メソッドの契約が満たされない場合に例外を使用する

メソッドが定められた振る舞いを行うことができない場合、例外によって失敗を報告すべきである。エラーコードの戻り値はたやすく無視されるし、エラーコードのチェックや伝播は正常系のコードを汚染し、中核となるロジックをわかりづらくする。

しかし、例外を通常の制御構造として使用してはいけない。このことは、publicメソッドを提供する際には、通常の使用で例外が投げられる確率をできるだけ減らさなければならない、ということでもある。

そのため、メソッドの事前条件が満たされるかテストするメソッドを別に提供し、例外を避けられるようにするのがよい(プログラマーが事前条件のテストを忘れれば例外が飛ぶが、これは正しい挙動である)。

internal static class Program
{
    public static void Main()
    {
        var worker = new DoesWorkThatMightFail();

        if (!worker.TryDoWork())
        {
            WriteLine("Fail");
        }

        WriteLine("Success");
    }
}

public class DoesWorkThatMightFail
{
    public bool TryDoWork()
    {
        if (!TestConditions())
            return false;
        Work();
        return true;
    }

    public void DoWork()
    {
        Work();
    }

    /// <summary>
    /// 事前条件のテスト
    /// </summary>
    /// <returns></returns>
    private bool TestConditions()
    {
        return true;
    }

    private void Work()
    {
    }
}

Effective C# 3rd 読書メモ 44 バインドされた変数を書き換えてはいけない

以下のコードで、sequence()デリゲートはindex変数をキャプチャしている。

public static void Main(string[] args)
{
    var index = 0;
    Func<IEnumerable<int>> sequence =
        () => Generate(30, () => index++);

    index = 20;
    foreach (int n in sequence())
        WriteLine(n);
    WriteLine("Done");

    index = 100;
    foreach (var n in sequence())
        WriteLine(n);
}

public static IEnumerable<int> Generate(int n, Func<int> func)
{
    for (var i = 0; i < n; i++)
        yield return func();
}

バインドされた変数を書き換えると、遅延実行との関係で予期せぬエラーを生むことがある。クロージャーにバインドされた変数の書き換えは避けるべきである。