こんにちは!トミセンです。
あまり使う機会がないと思われているnameof式ですが、
「C#でnameofって、使う意味あるの?」
「C#でnameofって、どうやって使うの?」
実は便利なコーディング手法なんですが、いまいち使いどころに迷ってしまいがちです。
今回はそんなお悩みを解決します。
ずばり!「C# nameofの使い方」についてご紹介します。
それでは一緒に学んでいきましょう!
C# nameofの使い方
まず、結論から。
文字列を指定する場面でnameofを使うことで、コードの修正漏れを防ぎ保守性の高いコードにすることができます。
実際に有効なケースを見ていきましょう。
nameof 演算子の対応バージョン
C# 6.0 以降(Visual Studio 2015 以降)
nameofを使わないソース
/// <summary>
/// モデル
/// </summary>
public class AmountModel
{
///<summary>コード</summary>
public int Code { get; set; }
///<summary>税抜き額</summary>
public decimal Amount1 { get; set; }
///<summary>税額</summary>
public decimal Amount2 { get; set; }
}
public class Test
{
public void Main()
{
// データ作成
var amountModel = new AmountModel { Code = 1111, Amount1 = 3000m, Amount2 = 300m };
// 税込み額計算
var amount = Calculate(amountModel);
// 結果
Console.WriteLine($"税込み額:{amount.ToString()}");
// 結果出力
// 税込み額:3300
}
/// <summary>
/// 計算
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <returns></returns>
public decimal Calculate<T>(T obj)
{
decimal amount = 0m;
Type type = typeof(T);
foreach (PropertyInfo property in type.GetProperties())
{
switch (property.Name)
{
case "Amount1":
case "Amount2":
amount += (decimal)type.GetProperty(property.Name).GetValue(obj, null);
break;
default:
break;
}
}
return amount;
}
}
実行すると、税込み額:3300が表示されます。
処理内容はAmountModelの項目のAmount1とAmount2を足して、税込み額を表示しています。
コードとコメントだけでは分かりにくい部分があるので、ポイントを絞ってコードを解説していきます!
計算する項目かどうかの判断にはまず、対象項目かの判定が必要になります。
その判定をしているのが次の場所になります。
public decimal Calculate<T>(T obj)
{
foreach (PropertyInfo property in type.GetProperties())
{
switch (property.Name)
{
case "Amount1":
case "Amount2":
amount += (decimal)type.GetProperty(property.Name).GetValue(obj, null);
break;
default:
break;
}
}
}
ここでは、クラスのプロパティ情報を取得して、項目名が文字列“Amount1”または“Amount2”の場合は、項目の値を取得し変数に値を足していきます。
ポイントは項目名を文字列で指定しているというところです。
現段階では文字列で指定しても問題はありません。
でもソースコードは変更されていきます。次のコードをみてください。
※通常はこんな面倒な方法で計算はしないと思いますが、説明のために作ったコードですので突っ込みはご遠慮ください。
/// <summary>
/// モデル
/// </summary>
public class AmountModel
{
///<summary>コード</summary>
public int Code { get; set; }
///<summary>税抜き額</summary>
public decimal TaxExcluded { get; set; }
///<summary>税額</summary>
public decimal TaxAmount { get; set; }
}
public class Test
{
public void Main()
{
// データ作成
var amountModel = new AmountModel { Code = 1111, TaxExcluded = 3000m, TaxAmount = 300m };
// 税込み額計算
var amount = Calculate(amountModel);
// 結果
Console.WriteLine($"税込み額:{amount.ToString()}");
// 結果出力
// 税込み額:0
}
/// <summary>
/// 計算
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <returns></returns>
public decimal Calculate<T>(T obj)
{
decimal amount = 0m;
Type type = typeof(T);
foreach (PropertyInfo property in type.GetProperties())
{
switch (property.Name)
{
case "Amount1":
case "Amount2":
amount += (decimal)type.GetProperty(property.Name).GetValue(obj, null);
break;
default:
break;
}
}
return amount;
}
}
変更された内容が、分かったでしょうか?
そうです。AmountModelのメンバが変更されています。
誰かが分かりにくいと指摘したのでしょう。Amount1とAmount2が、TaxExcludedとTaxAmountというに変更されています。
ここで問題発生してしまいました。
var amountModel = new AmountModel { Code = 1111, TaxExcluded = 3000m, TaxAmount = 300m };
コンパイルエラーになるAmountModelの生成箇所は変更されましたが、金額計算をしているCalculateメソッドのswitch文はAmount1とAmount2のままです。
そのため、実行すると、税込み額:0が表示されます。
1人で開発しているなら、Calculateメソッドも対象だとすぐに気づいたでしょう。
でも1人の人がそのシステムをずっと担当するなんてことはありません。
他の人が関われば漏れてしまう場合もあります。では、どうすればよかったのでしょうか。
ここでnameofが活躍します。
nameofを使ったソース
switch (property.Name)
{
case nameof(AmountModel.Amount1):
case nameof(AmountModel.Amount2):
amount += (decimal)type.GetProperty(property.Name).GetValue(obj, null);
break;
default:
break;
}
switch文でnameofを使っていれば、問題を発見することができていたはずです。
使い方はnameof(AmountModel.Amount1)のようにnameof(クラス名.メンバ名)のように書きます。
意味は、"Amount1"と書いたのと同じですが、AmountModelからAmount1がなくなるとコンパイルエラーを表示してくれます。
Visual Stuioの実際の画面です。
エラーになっているのが確認できると思います。もちろんビルドも通りません。
つまり、変更によるバグに気づかずにリリースしてしまうというようなことがなくなります。
以上が、nameofの使い方です。
その役割は、コードの修正漏れを防ぎ保守性の高いコードにすることです。
まとめ
保守性の高いコードは重要ですよね。
それでは、また。