秒でインスタンス変数を読み取り専用にする by Visual Studio

## この記事は

[Qiita Engineer Festa 2022](https://qiita.com/official-campaigns/engineer-festa/2022)のテーマ「**悪いコードを改善した時の体験や知見を共有しよう**」に寄せて書いた記事です。

具体的なコードには言及せず、**Visual Studioを使って手軽にできるプチ改善方法**を共有します。

👉対象読者： **Visual StudioとC#を使っている初心者エンジニア**

https://qiita.com/official-events/535fdbb5cb67791998f8

## コード改善の基本スタンス

一言で「悪いコード👿」といっても、いろいろな種類の悪さ／症状があるかと思います。
それらの症状に広く適用できる改善方法の１つが・・・

「**インスタンス変数はなるべく不変にしよう！！**」です✨

```csharp
// 不変とは、再代入を禁止すること

// 再代入とは
int a = 1 // 宣言したあとに
a = 2     // 違う値を代入すること
```

### インスタンス変数が可変であると起こりうるリスクについて

ある程度大きいクラスになると、各メソッドが同じインスタンス変数を使う確率があがります。
あるとき、変数の値を書き換えるメソッドが出現します。

```plantuml
class BigClass
{
    - string name
    - int SomeMethod1()
    - int SomeMethod2()
    .. {中略} ..
    - int SomeMethod9()
}
note right: SomeMethod1～9はインスタンス変数 name を使うメソッド\nあるとき、修正が入り SomeMethod9 は name を変更するようになった
```

同じクラスに、**変数の値が書き変わらないことを前提に作ったメソッドがあった場合**、そのメソッドは予期せぬ結果を返します。
予期せぬ結果はいずれ表面化し、品質リスクになります。

```plantuml
class BigClass
{
    - string name
    - int SomeMethod1()
    - int SomeMethod2() : nameが変更されないことを前提につくったメソッド
    .. {中略} ..
    - int SomeMethod9() : nameを変更するメソッド
}
note right: SomeMethod2()を呼ぶタイミングによっては予期せぬ動作をする\n（原因はSomeMethod9を呼ぶタイミングにあった）
```

変数を書き換えるメソッドを特定して、タイミングを見計らってメソッドを使ってもらうよう周知します。
しかしそのアナウンスはいずれ忘れ去られます。
そして不幸は繰り返されます😱

## 変数を不変にしよう、そうしよう

変わるから予期できないんだ。だったら変えることを禁止してしまえばいい😀

**変数の頭に`readonly`修飾子をつけます！**　　※ `C#`の場合

```cs
readonly int abc; // 読み取り専用の変数 abc
```

### でも、変数を不変にするのが怖い

恐る恐る、巨大クラスの上のほうにあった変数に`readonly`をつけた
**コードがエラーで真っ赤になった！** 😨

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272511/43d628c1-d5c3-f7d6-eddf-eeaa85dfc343.png)

### 変数が多すぎて、どれを不変にしていいかわからない

おもいきって、置換機能ですべての変数に`readonly`をつけた。
**コードがエラーで真っ赤になった！！** 😨（2回目）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272511/43d628c1-d5c3-f7d6-eddf-eeaa85dfc343.png)

## 【解決策】Visual Studioに助けてもらおう

IDE, 統合開発環境のチカラを借ります🔧

手順はたった２つです。20秒でできます⏰

### コードクリーンナップの構成

Visual Studioの検索窓に「**コードクリーンナップの構成**」と入力、クリックするとダイヤログが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272511/144c372a-40c1-0e15-9545-154aa4e37d64.png)

**フィールドを読み取り専用にします**を追加してOK

![image](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272511/2ad01d14-eb5a-311b-d8f0-802ac0c5dce3.png)

### コードに戻って、`Ctrl+K` + `Ctrl+E`を押す

`Ctrl`に小指を置いたまま、`K`，`E`の順に押すと・・・

![animation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272511/3192bbc9-da86-9c2e-cb56-288cbe86e946.gif)

**読み取り専用にできる変数には`readonly`が勝手に付与された！** 🎉
（`width`や`height`のこと）

一方で、`count`はメソッドの中で変更されるので、`readonly`対象外。

もちろんこのままコンパイルも通ります。先ほどのようにエラー続出にはなりません。

## このあとどうする？

`count`をどうにかできないか考えます。リファクタリングというものです。

- インスタンス変数ではなく、メソッド内のローカル変数して**スコープを小さく**できないか？
  - できないなら、`count`を変更するだけのメソッドを用意する
    - **変数がどこで使われているか**よりも、**メソッドがどこで使われているか**を追うほうが簡単
  - メソッドに切り出す際は、**副作用（冪等性）**や**コマンドクエリ分離原則**を意識する

## おわりに

Visual Studioには、**無駄な条件分岐をシンプルに**したり、**名前の変更を一括で行ったり**と、コードの改善を手助けしてくれる機能がたくさんあります。
どんどん活用していきたいですね🙌

手前みその関連記事：[Visual Studio 2019 を入れて最初に行う設定](https://qiita.com/t13801206/items/481db1a82585483bb1a3)

## おまけ

`readonly`以外にも`in`や`IReadOnlyCollection<T>`を使うと、読み取り専用スキルがアップ！！

https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/in-parameter-modifier

https://qiita.com/lobin-z0x50/items/248db6d0629c7abe47dd

## エピローグ

「**悪いコードを改善した時の体験や知見を共有しよう**」を初めに見たとき
新人のころに出会った数々のコードが頭に思い浮かんだ

しかし、いざ再現して悪いところを指摘しようとしても、**なかなか言語化できないことに気づいた**
変更するのが凄い手間だったり、変更して正常に動かなくなるのが怖かったのは確かなのに
いざ**どこをどうすればよかったのか**を考えると、手が止まる

おそらくその理由の１つは、**どうしようもなくダメなところが多すぎて１記事にはおさまらない**ことだと思う（やや言い訳がましいが）
ex)ベタ書きやグローバル変数を使うより、ユースケースごとにクラスを分けて、クラスごとに使う変数だけをDIしたリポジトリから引っ張ってきて、、、orz

もう１つの理由は、やはり**自分がその改善策に自身がもてないから**だと反省。勉強します。

そして結局は**手軽に明日から使える手先の小ネタに逃げた**記事になった

何かコード的にアブナイことをしている人（またはチーム）に対して
「これは〇〇という場合に▼▼ということが起きてアブナイから■■にしてみたら？」と言うのは簡単だけど、その■■の裏にも前提となるベストプラクティスがあって
順序立てて説明（教育）しないと納得してもらえない。「なんで動いているのにそんな遠回りをするんだ」と言われる

つまり

> 人は、自分がよく分かっていないことは正しく説明できないですし、曖昧でぼんやりするものです。
> より正しく精密に理解しているものほど、的確に言語化できます。
> 引用元：https://qiita.com/official-events/535fdbb5cb67791998f8

を身に染みてわかった取り組みになりました。おわり。