Enum ではなく Class を使う

Enum を使うと switch ... case ... することになる。

たとえば、プログラム言語を Enum で定義して、その名前を表示する例。

using System;

namespace UseClassInsteadOfEnum
{
    public enum ProgrammingLanguageEnum
    {
        CSharp,
        Java,
    }
    
    public class ByEnum
    {
        public static void PrintName(ProgrammingLanguageEnum progLang)
        {
            switch (progLang)
            {
                case ProgrammingLanguageEnum.CSharp:
                    Console.WriteLine("C#");
                    break;
                case ProgrammingLanguageEnum.Java:
                    Console.WriteLine("Java");
                    break;
                default:
                    throw new ArgumentException("progLang");
            }
        }
    }
}

Enum だと、

  • なにかするには switch ... case ... を書かないと、、、
  • 一応 default も書いておかないと心配だし、、、
  • 将来 Enum のメンバを変更したら、全部調べて修正しないと、、、

と、悩むことになる。

クラスにすると、スッキリする。

using System;

namespace UseClassInsteadOfEnum
{
    public class ProgrammingLanguageClass
    {
        // 各項目を表わすインスタンスは static にする。
        private static ProgrammingLanguageClass _cSharp = new ProgrammingLanguageClass("C#");

        public static ProgrammingLanguageClass CSharp
        {
            get { return _cSharp; }
        }

        private static ProgrammingLanguageClass _java = new ProgrammingLanguageClass("Java");

        public static ProgrammingLanguageClass Java
        {
            get { return _java; }
        }

        private String _name;

        // コンストラクタは private にして、クラス外からインスタンスが作れないようにする。
        private ProgrammingLanguageClass(String name)
        {
            _name = name;
        }

        public String Name
        {
            get { return _name; }
        }
    }

    public class ByClass
    {
        public static void PrintName(ProgrammingLanguageClass progLang)
        {
            Console.WriteLine(progLang.Name);
        }
    }
}

使う側からは、同じように見える。

namespace UseClassInsteadOfEnum
{
    class Program
    {
        static void Main(string[] args)
        {
            ByEnum.PrintName(ProgrammingLanguageEnum.CSharp);
            ByClass.PrintName(ProgrammingLanguageClass.CSharp);
        }
    }
}

Visual Studio のスニペット

スニペットを使えば、決まった内容を簡単に入力できる。

たとえば、C# のプロパティを作成する場合、"propfull" と入力し、

f:id:tt195361:20150603132233p:plain

TAB キーを押すと、

f:id:tt195361:20150603132310p:plain

こんなふうに展開してくれる。あとは、反転表示されている部分を編集する。

  • TAB キーで次の反転表示に移動
  • Enter キーでスニペット編集終了

よく使うのは、"if", "for", "foreach", "try", "tryf" など。

"Tools -> Code Snippets Manager..." で、どんなスニペットがあるか確認できる。

f:id:tt195361:20150603133225p:plain

単体テスト用のものもあるとは、知りませんでした。

プログラムをオブジェクト指向にするには

プログラムをオブジェクト指向にするには、

  • そのプログラムがなにをするか、に加えて、
  • それをどこにやらせるか、

が重要になる。

気を付けるのは、他のクラスのプロパティを使って、いろいろ計算しているところ。次の例では、適用期間 (ApplicableTerm) クラスを使うクラスが、ApplicableTerm の Start と End プロパティから期間内かどうかを求めている。

using System;

namespace ObjectOrientedBadSample
{
    public class ApplicableTerm
    {
        private TimeSpan _start;
        private TimeSpan _end;

        public TimeSpan Start
        {
            get { return _start; }
        }

        public TimeSpan End
        {
            get { return _end; }
        }
    }

    public class ApplicableTermUser
    {
        public void BadUser(ApplicableTerm term)
        {
            TimeSpan someTimePoint = new TimeSpan();    // ある時点
            if (term.Start <= someTimePoint && someTimePoint <= term.End)
            //  ========================================================
            {
                // 適用期間内
            }
        }
    }
}

他のクラスのプロパティを使って自分で計算するのではなく、そのプロパティを提供するクラスにやってもらう。

using System;

namespace ObjectOrientedBetterSample
{
    public class ApplicableTerm
    {
        private TimeSpan _start;
        private TimeSpan _end;

        public bool IsIncluded(TimeSpan timePoint)
        {
            return _start <= timePoint && timePoint <= _end;
        }
    }

    public class ApplicableTermUser
    {
        public void GoodUser(ApplicableTerm term)
        {
            TimeSpan someTimePoint = new TimeSpan();    // ある時点
            if (term.IsIncluded(someTimePoint))
            //  ==============================
            {
                // 適用期間内
            }
        }
    }
}

知識レベル-操作レベルを使うとき(1)

アナリシスパターンの知識レベル-操作レベルを使うときとして、

  • プログラムの再コンパイルなしで、
  • なにかの種類を増減したい。

場合がある。

たとえば、測定の種類として身長や体重などがあるとする。これらがプログラムのコンパイル時点で決定できるならば、以下のように enum で表現できる。

namespace KnowledgeOperation1
{
    public enum MeasurementTypeEnum
    {
        Height,
        Weight,
        // . . . . .
    }

    public class WrittenInSourceCode
    {
        MeasurementTypeEnum _measType;
    }
}

測定の種類が増減し、プログラムを再コンパイルせずに対応するには、

  • 測定の種類をクラスにして、
  • そのインスタンスを生成・破棄する

ようにしておけばよい。

namespace KnowledgeOperation1
{
    // new MeasurementTypeClass("Height");
    // new MeasurementTypeClass("Weight");
    // . . . . .

    public class MeasurementTypeClass
    {
        public MeasurementTypeClass(string name)
        {
            //
        }
    }

    public class UsingReference
    {
        MeasurementTypeClass _measType;
    }
}

参考: アナリシスパターンを読もう

Windbg で sosex を使ってマネージコードのソースレベルでブレークポイント設定

Windbg でマネージコードをデバッグするとき、sosex を使えば、ソースレベルでブレークポイントを設定できる。

  1. '.load sosex.dll' で sosex をロード。
  2. '!mbp ソースファイル名 行番号' でブレークポイント設定。
  3. 'g' で実行開始。
  4. ブレークポイントにヒットした。
  5. '.loadby sos.dll clr' で sos をロード (フレームワークが 3.5 までは 'clr' を 'mscorwks' に変更する、参考: Unable to load SOS in WinDbg)
  6. '!ClrStack' でマネージスタックを表示。
  7. 指定の位置でブレークしている!

f:id:tt195361:20150528102143p:plain

Visual Studio のインクリメンタルサーチ

Visual Studioインクリメンタルサーチを使えば、1文字打つごとにカーソルが移動し検索できる。

  1. Ctrl + I でインクリメンタルサーチ開始。マウスカーソルが双眼鏡の形に変わる。
  2. サーチする文字を入力すると、その文字へカーソルが移動する。下の例では 'c' を入力。
  3. 次の文字を入力すると、さらにカーソルが移動する。例では 'a' を入力し 'ca' へ移動。
  4. サーチ中に Ctrl + I を入力すると、次のサーチ文字列へカーソルが移動する。例では次の 'ca' へ移動。
  5. ESC を押すと、インクリメンタルサーチ終了。

f:id:tt195361:20150528082220p:plain