sh1’s diary

プログラミング、読んだ本、資格試験、ゲームとか私を記録するところ

複数の bool 値と byte 値を相互に変換する

DB を設計していると、ひとつの列で bool 値をまとめてしまおうという発想をすることがある。ほとんどのケースで、これは設計ミス(アンチパターン)となりやすいことで知られ、jaywalking(信号無視)パターンと言います。

どうして jaywalking パターンは、悪いのか。

カラムに文字数制限があれば(わかりづらい)上限になること、格納されるカラム名が無いから仕様書がないと情報が減ること(=誤ったデータを挿入して運用を進めると困ったことになる)……色々考えられると思います。

なので、本当だったらあんまり使いたくない。でも、既存 DB で使われている場合や、参照の少ない履歴を管理するテーブルで(僅かな)容量削減を目的に利用されてたりする。さて、どうしよう。

リファクタリングするにしても、リファクタリングをする価値のないデータやプロジェクトであるなら、そこに時間を投資しても困る。したくない。残念ながら、(体力だったり、政治だったりを含め)色々な理由でやむを得ないケースはある。

そんなわけで、DB 上に存在することを想定した byte (int) 型のデータを複数の bool 値に変換したり、その逆をしたりするサンプルです。

byte to bool[8]

private bool[] ToBools(byte data)
{
    var bools = new bool[8];

    for (int i = 0; i < 8; i++)
    {
        bools[i] = (data & (1 << i)) != 0;
    }

    Array.Reverse(bools);

    return bools;
}

bool[8] to byte

private byte ToByte(bool[] sources)
{
    byte result = 0x00;
    int index = 8 - sources.Length;

    if (sources.Length <= 0)
    {
        return result;
    }

    foreach (var source in sources)
    {
        if (source)
        {
            result |= (byte)(1 << (7 - index));
        }

        index++;
    }

    return result;
}

サンプル

WPF の binding を利用するとこんな感じになると思います。IsEnabled1 ~ IsEnabled8 は、8つのチェックボックス IsChecked プロパティにそれぞれ対応。

public byte Result
{
    get
    {
        var bools = new bool[8];

        bools[0] = IsEnabled1;
        bools[1] = IsEnabled2;
        bools[2] = IsEnabled3;
        bools[3] = IsEnabled4;
        bools[4] = IsEnabled5;
        bools[5] = IsEnabled6;
        bools[6] = IsEnabled7;
        bools[7] = IsEnabled8;

        return ToByte(bools);
    }
    set
    {
        var bools = ToBools(value);

        IsEnabled1 = bools[0];
        IsEnabled2 = bools[1];
        IsEnabled3 = bools[2];
        IsEnabled4 = bools[3];
        IsEnabled5 = bools[4];
        IsEnabled6 = bools[5];
        IsEnabled7 = bools[6];
        IsEnabled8 = bools[7];
    }
}

これも当然ながら、jaywalking パターンらしい欠点を持つ byte 型を格納することになる。

参考