c#でプロパティがref/outなメソッド引数に指定できない件

例えば以下のように宣言/利用をしたとする。

// 自動実装プロパティ
public int No { get; set; }

public void Sample(string message) {
    if(!int.tryParse(message, out No) {
        throw new ArgumentException("Failed to parse.");
    }
}

しかし、実際に以下のエラーとなり指定ができない。

プロパティ、インデクサー、または動的メンバー アクセスを out または ref のパラメーターとして渡すことはできません。

この挙動は自動実装プロパティが実際はsetter/getterメソッドを隠ぺいした存在という事に起因する。このままではコンパイルが通らないため、特に何も考えず以下のように修正を行うことにする。

// 自動実装プロパティ
public int No { get; set; }

public void Sample(string message) {
    int result;
    if(!int.tryParse(message, out result) {
        throw new ArgumentException("Failed to parse.");
    }
    this.No = result;
}

外部ライブラリのクラスフィールドはの値は、大抵の場合、プロパティ経由で提供される。従って、このようなケースに年に数度遭遇する。このページでは解決を試みているようだが、毎回このような宣言を書くのは相当手間だし利用者にも理解されまい。

本来宣言する必要性の無い一時変数を排除し、自動実装プロパティに直接値を設定するためい、いくつか欠点はあるが以下の実装を考案する。

// Stringクラスに対する拡張メソッド宣言
public static class String {
    public static bool TryToInt32(this string s, int num) {
        return int.TryParse(s, out flag);
    }
}

// --- 利用者側コード ---
// 自動実装プロパティ
public int No { get; set; }

public void Sample(string message) {
    if(!message.TryToInt32(No)) {
        throw new ArgumentException("Failed to parse.");
    }
}

本来あればint.Paese()のオーバーロードを追加したいところだが生憎、静的メソッドの拡張は許されていない。必然的に以下の2つの欠点が発生している。

  1. messageを主語として利用するため、変数がnullの場合はNullReferenceExceptionが発生する。
  2. 成功すればnumに値が設定される副作用がコード上に明示できない。

リフレクションを使えばもう少し気が効いた事も可能だろうが、変数1つ設定するためにそのようなコストを払う気は起きない。現状ではこの実装が最善と思われる。