最近過去の自分のコードを見ていて少し思うところがあり,その中で気づいた点の1つです.新しく覚えた書き方の便利さに触れた時に陥りやすい罠かもしれないと思います.
C#で,メソッドの定義は通常このような形で行います.
public static bool IsElon(User user)
{
return user.Id == "elonmusk";
}
ただ,このようなシンプルなメソッドの定義をこのように書くのは間延び感があります.そのようなケースのため,以下のような文法が用意されています.
public static bool IsElon(User user) => user.Id == "elonmusk";
このような形式を「式形式」と呼びます.
読みやすいパターン
タイトルでは「読みづらい」と言いましたが,シンプルで短いメソッドに対してはとても効果的な書き方です.
-
ごく簡単な操作を行うメソッドに対して使う場合
// public bool IsDirty { get; private set; } public void SetDirty() => IsDirty = true; -
getterやsetterを書く場合
// private bool _isDirty; public bool IsDirty { get => _isDirty; private set => _isDirty = value; }
こういった場面では,書き手読み手双方にとって便利な書き方です.
読みづらいパターン
ではどのような場面で読みづらくなるかというと,メソッドの内容が単純ではない場合です.
public static bool CanMove(Player player, bool ignoreMenu) =>
player != null &&
player.AmOwner && (
ignoreMenu || (
!OptionsMenu.Instance.IsOpening &&
!PopupManager.Instance.IsShown)) &&
!player.Animator.IsAnimating;
見ているだけでうんざりしてきます.このような場合は無理して式形式を使わず,多少長くなっても素直にこう書くべきです.
public static bool CanMove(Player player, bool ignoreMenu)
{
if (player == null)
{
logger.LogDebug("playerがnull");
return false;
}
if (!player.AmOwner)
{
return false;
}
var isMenuOpening = OptionsMenu.Instance.IsOpening || PopupManager.Instance.IsShown;
if (!ignoreMenu && isMenuOpening)
{
return false;
}
return !player.Animator.IsAnimating;
}
このように書く方が,行数の少なさにこだわるよりも遥かに利点が多いです.式形式では,1つの式に収めなければならないという制約がありますが,そういった制約を気にせず書けます.この例で示されただけでもざっと以下のようなメリットがあります.
- 途中でログの出力や例外の送出を行える
- 早期リターンを使える
- 前提条件,このような場合は常にfalseになるよ,というような意図が汲み取りやすくなる
- 要約変数(
isMenuOpening)を使える- 「メニュー」以外に様々な状態を考慮する必要があるような場合でも,どこからどこまでがメニューに関する判定をしているのか,といったことが読み取りやすくなる
- 今後「メニュー」に該当するものが増えた際は
isMenuOpeningの右辺に追加されるため,メニュー開閉の条件を増やしたことがわかりやすい- メソッドの分割やメニューのリファクタを行うと尚良
結論
式形式の書き方は単純なメソッドに対しては強力ですが,濫用するとかえってコードを複雑に入り組んだ読みにくいものにしてしまい,バグの原因にもなります.
個人的な基準としては,式形式にしたときに2行以上にわたるようなメソッドや,複数の条件が絡み合うようなメソッドには使用を避けています.
プロジェクト内で明確に決められていない限り,使用は本当に単純なメソッドに限り,読みやすさに十分に気をつけて使うのが無難です.