PG日誌

受託系 PG が C# の事を書いています

外部に公開しているメソッドは引数チェックをしよう

引数チェックって大切ですよね?オブジェクトが健全な状態を保つためにも外部に公開してる操作は引数チェックを行いましょう。対象は以下となります。

  • チェックすべき操作
    • public なクラス *1
    • public | protected なメソッドもしくはプロパティ *2

チェックしない例

たとえば以下例です。

using using System.IO;

public class Sample 
{
    public static void DoWork(string path, int count)
    {
        for(int i = 0; i < count; i++)
        {
            File.Copy(path, 
                Path.Combine(Path.GetDirectoryName(path), 
                    count + "_" + Path.GetFileName(path)));
        }
    }
}

path で指定したファイルパスに count で指定した数だけファイルをコピーします。この時

  • pathに
    • nullや空文字を渡してもいいのか?
    • ディレクトリを渡した場合どうなるのか?
  • countに
    • -1 が指定されていいのか?
    • 10万などの大きい数字は指定してよいのか?
  • チェックせずに受け入れた場合
    • 例外(StackTraceやMessage)を見て原因が分かるのか?
    • エラーが起きた時にシステムが壊れないか?

など、いろいろ考えられると思います。path に Empty が入っていた場合、最初に

Path.GetDirectoryName(path),

が評価され

ArgumentException : パスの形式が無効です。

と表示されますが、.NET の例外はパラメータの内容まで面倒見てくれないので、VisualStudio 上以外で動作していると調査がやや面倒な場合があります。

チェックする例

上記の課題を解消するためにチェックを追加します。

using using System.IO;

public class Sample 
{
    public static void DoWork(string path, int count)
    {
        // 開始前に引数をチェックする --->
        if (string.IsNullOrWhiteSpace(path))
        {
            throw new ArgumentException("引数が null もしくは empty です。", nameof(path));
        }

        if (!File.Exists(path))
        {
            throw new FileNotFoundException("ファイルが見つかりません。", path);
        }

        if (count > 100 || count <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(count), 
                count + " は範囲外です。1から10までが指定できます。");
        }
        // <--- 開始前に引数をチェックする
        
        if(string.In
        for(int i = 0; i < count; i++)
        {
            File.Copy(path, 
                Path.Combine(Path.GetDirectoryName(path), 
                    count + "_" + Path.GetFileName(path)));
        }
    }
}

引数チェックをする場合、こういったコードを何回も書くことになると思うので、チェックはどこかにまとめるといいと思います。

Assert を使う【非推奨】

第三の手段として Assert(アサーション)も使えます。Release ビルド時には取り除かれるため、リリース時に無駄な処理になりません。

using System.Diagnostics; // !Add

public class Sample 
{
    public static void DoWork(string path, int count)
    {
        // アサーションによるチェック
        Debug.Assert(!string.IsNullOrWhiteSpace(path), nameof(path) + " の引数が null もしくは empty です。");
        Debug.Assert(File.Exists(path), "ファイルが見つかりません。[" + path + "]");
        Debug.Assert(!(count > 100 || count <= 0), count + " は範囲外です。");
        
        if(string.In
        for(int i = 0; i < count; i++)
        {
            File.Copy(path, 
                Path.Combine(Path.GetDirectoryName(path), count + "_" + Path.GetFileName(path)));
        }
    }
}

プログラミングしているときは効果的ですが、稼働時やサーバー上で動いているとき(VisualStudio外で動作)しているときに、追跡が難しいため利用は計画的に行いましょう。

*1:internalや内部クラスは除外します。お好みでチェックするか決めましょう。

*2:変数の直接公開は無いと思っています。