Visual Studio Tips集 (for C# & VB)

# はじめに
この記事では、知っておくとちょっと作業が楽になるVisual Studio（CodeではないIDEのほう）のTipsを紹介します。

## 検証環境・プロジェクト設定
- Visual Studio Professional 2019 16.11.4
    - キーボードショートカットはデフォルト状態
- .NET Framework 4.8
- **C#, Visual Basic**で確認（一部機能は言語によって使えなかったり、挙動が異なる可能性がある）

## 表記方法について
- `Ctrl+R, Ctrl+R`のように記載している場合は、`Ctrl+R`を2回押すことを意味する。
    - 修飾キーを押し直す必要はなく、1回目の`Ctrl+R`を押した後`Ctrl`を離さずにもう1回`R`だけ押し直せばOK。
- 「**[クイック アクションとリファクタリング] メニューを開く**」は、以下のいずれかの操作を意味する。
    - 対象箇所を右クリックして[クイック アクションとリファクタリング]を選択する。
    - 対象箇所にカーソルを置いた状態で、左側（行数表示部分）に現れるドライバーアイコンをクリックする。
    - 対象箇所にカーソルを置いた状態で、`Ctrl+.` を押す。

# リファクタリング機能
## 全般的な注意事項
- コンパイルエラーになるような箇所（赤線）があると、正しく動作しない可能性がある。
- 他ソリューションにおける使用箇所や、リフレクションで呼び出しているような箇所は捕捉できない。

## 名前の変更
識別子（変数、メソッド、プロパティ、クラス等）の名前を変更できる。
そのクラスやメンバが複数ファイルにまたがって参照されている場合でも、すべての出現箇所を安全に変更できる。
![RenameIdentifier.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426590/66b33aa6-6d16-d596-ced8-1ee7407848f7.gif)

1. 対象の識別子にカーソルを置く。
1. 右クリックして「名前の変更」を選択するか、 `Ctrl+R`（RenameのRと覚えるとよい）を2回連続で押す。
1. 対象の識別子と変更の対象になる箇所が緑色でハイライトされるので、そのまま新しい名前をタイプする。
1. [適用]をクリックするか、Enterキーを押して確定させる（Escキーでキャンセル可能）。

### オプション
- **コメントを含める**：変更対象にコメントを含める。
- **文字列を含める**：変更対象に文字列リテラルを含める。
- **シンボルファイルの名前を変更する**（クラス、VBモジュール限定）：このクラスが記載されているファイルの名前もあわせて変更する。

### 注意点
- 「コメントを含める」、「文字列を含める」は、使い方によっては意図しない箇所も変更してしまう場合がある。
    - 画像の例では、`namae` → `myName` の変更をした際に、 24行目（`DoAnotherThing`内）の`"namae"`も置換されている。

## 署名（シグネチャ）の変更
メソッド引数の追加・並び替え・削除を行うことができる。
すでにそのメソッドを呼び出している側の引数もあわせて変更される。
![ModifySignature2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426590/b8991685-8280-887e-d22f-1b7352a6e659.gif)

1. 対象メソッドの宣言部にカーソルを置く。
1. [クイック アクションとリファクタリング] メニューを表示し、[署名の変更]を選択する。<br />または、`Ctrl+R, Ctrl+V` を押す。
1. 「署名の変更」ダイアログが表示されるので、引数の追加・並び替え・削除を行う。
1. [OK]をクリックすると、変更が確定される。

### オプション
引数追加の場合は、呼び出し側でその引数に何を与えるか指定できる。

- **値**：入力した値をそのまま挿入する。
    - 名前付き引数の使用：`メソッド名(引数の名前: 値)` (C#)、`メソッド名(引数の名前:=値)` (VB) のように挿入される。
- **未定義のTODO変数の導入**：`TODO` を挿入する。当然呼び出し側にそのような変数がなければコンパイルエラーになるので、適宜修正する。
- **コンテキストから推論する**：呼び出し箇所前後の内容（変数やその型）などから適切な値を推論する。推論できない場合は`TODO`になる。
- **省略(省略可能なパラメーターの場合のみ)**：[既定値を含むオプション]を選択している場合のみ利用可能。

## 引数の折り返し・折り返し解除
メソッドに複数の引数があるとき、カンマのところで改行させたり、逆に改行せず1行につなげることができる。
![Wrap-UnwrapParam.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426590/d58add2d-2659-668b-0adc-254f4eea32c9.gif)

1. 対象メソッドの宣言部にカーソルを置く。
1. [クイック アクションとリファクタリング] メニューを表示し、[すべてのパラメーターを折り返します]などの選択肢を選ぶ。

また、この機能は呼び出し側でも利用できる。
![Wrap-UnwrapParams-Call.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426590/b2295f4b-2449-0dbd-3037-907f867b30e0.png)

### オプション
#### 折り返し
```cs:折り返されたパラメーターを調整します/折り返された引数を揃える
//宣言
void ShowMyName(string message,
                int age,
                bool detailMode)

//呼び出し
ShowMyName("aaa",
           20,
           true);
```
```cs:すべてのパラメーターにインデントを設定します/すべての引数にインデントを設定します
//宣言
void ShowMyName(
    string message,
    int age,
    bool detailMode)

//呼び出し
ShowMyName(
    "aaa",
    20,
    true);
```
```cs:折り返されたパラメーターにインデントを設定します/折り返された引数にインデントを設定します
//宣言
void ShowMyName(string message,
    int age,
    bool detailMode)

//呼び出し
ShowMyName("aaa",
    20,
    true);
```
#### 折り返し解除
```cs:すべてのパラメーターの折り返しを解除します/すべての引数の折り返しを解除
//宣言
void ShowMyName(string message, int age, bool detailMode)

//呼び出し
ShowMyName("aaa", 20, true);
```
```cs:折り返しを解除し、すべてのパラメーターのにインデントを設定します/折り返しを解除してすべての引数にインデントを設定します
//宣言
void ShowMyName(
    string message, int age, bool detailMode)

//呼び出し
ShowMyName(
    "aaa", 20, true);
```

## メソッドチェーンの折り返し・折り返し解除
LINQなどで長いメソッドチェーンができた場合に、それを見やすく折り返すことができる。また、折り返し解除もできる。
![WrapMethodChain.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426590/1b3502d3-8189-1818-14dc-a895f242cbed.gif)

1. 対象メソッドチェーンの途中にカーソルを置く。
1. [クイック アクションとリファクタリング] メニューを表示し、[呼び出しチェーンの折り返しと配置]などの選択肢を選ぶ。

### オプション
```cs:呼び出しチェーンの折り返し
return list.Where(x => x.Contains("aaa"))
    .Distinct()
    .Select((x, i) => $"{i}番目:{x}")
    .Skip(2)
    .FirstOrDefault();
```
```cs:呼び出しチェーンの折り返しと配置
return list.Where(x => x.Contains("aaa"))
           .Distinct()
           .Select((x, i) => $"{i}番目:{x}")
           .Skip(2)
           .FirstOrDefault();
```
```cs:長い呼び出しチェーンの折り返し
return list.Where(x => x.Contains("aaa")).Distinct().Select((x, i) => $"{i}番目:{x}").Skip(2)
    .FirstOrDefault();
//1行が一定の長さを超える場合に、その直前の.で折り返す
```
```cs:長い呼び出しチェーンの折り返しと配置
return list.Where(x => x.Contains("aaa")).Distinct().Select((x, i) => $"{i}番目:{x}").Skip(2)
           .FirstOrDefault();
```
```cs:呼び出しチェーンの折り返し解除
return list.Where(x => x.Contains("aaa")).Distinct().Select((x, i) => $"{i}番目:{x}").Skip(2).FirstOrDefault();
```

※VBでも折り返された行の先頭は `.` から始まる。折り返し箇所には行継続文字 `_` が挿入される。

## if文、条件式の反転
if文の条件を反転できる。
![ReverseIf.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426590/5e630efc-4d79-90c0-c3f3-d1023e777872.gif)

1. if文の条件式部分にカーソルを置く。
1. [クイック アクションとリファクタリング] メニューを表示し、[ifを反転する]を選ぶ。

AND/ORや比較演算子を含むなど条件が複雑な場合でも、自動的に逆の条件式に書き換えてくれる。
![ReverseIf2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426590/139734d1-b2d9-d576-ab96-c0437efc133a.png)
条件演算子を使った式でも利用できる。
![ReverseIf3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426590/41dedc05-becf-6eda-02eb-606c022bcf87.png)
![ReverseIf3VB.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426590/55fa7186-673e-5a31-8b81-5e7e43f1b43e.png)

## for/foreach文の相互変換
コレクションを順次処理するループのfor文をforeach文に書き換えたり、逆にforeach文をfor文に書き換えることができる。
![ConvertFor-ForEach.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426590/ede61c88-5f00-b744-1829-b6c1b1b5e3a6.gif)

1. for/foreachキーワードにカーソルを置く。
1. ['foreach' に変換する]または['for' に変換する]を選ぶ。
1. 必要に応じ変数の名前変更モードに入るので、適切な名前を入力し`Enter`キーで確定する。

## リテラルから定数を作成する
リテラルで直接値を指定している箇所から定数を作成し、その定数を使用するように変更できる。
![IntroduceConst.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426590/3d83204d-d53c-9e82-cbca-d651dbcdc22c.gif)

1. リテラルを使用している箇所にカーソルを置く。
1. [クイック アクションとリファクタリング] メニューを表示し、以下のオプションのいずれかを選ぶ。
1. 定数が作成される。自動的に名前変更モードになるので、適切な名前を入力し`Enter`キーで確定する。

### オプション
- **'（リテラル）' に対して定数を導入します**：現在のクラスに新規プライベート定数を作成して、カーソルを置いた箇所のみその定数に置き換える。
- **'（リテラル）' のすべての発生に対して定数を導入します**：現在のクラスに新規プライベート定数を作成して、同じクラス内に存在するすべての同一リテラルを定数に置き換える。
- **'（リテラル）' に対してローカル定数を導入します**：現在のメソッドに新規ローカル定数を作成して、カーソルを置いた箇所のみその定数に置き換える。
- **'（リテラル）' の全ての発生に対してローカル定数を導入します**：現在のメソッドに新規ローカル定数を作成して、同じメソッド内に存在するすべての同一リテラルをその定数に置き換える。

## ローカル変数の抽出
リテラルと変数を組みあわせている箇所からローカル変数を作成し、その変数を使用するように変更できる。
使用手順などは上記「リテラルから定数を作成する」と同じ。
![IntroduceLocalVariable.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426590/adf7fb75-287f-0121-eb69-7a431cbf645e.gif)

## ローカル変数の廃止とインライン化
1回しか使用されない、要約変数的な機能も薄いなどで必要無くなったローカル変数を削除し、使用箇所に直接内容を記載する形に変更できる。
![LocalVariableToInline.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426590/3f309eca-6aa6-3739-a677-bc0ff504de3d.gif)

1. 対象ローカル変数の宣言部にカーソルを置く。
1. [クイック アクションとリファクタリング] メニューを表示し、[インラインの一時変数]を選択する。

### 注意事項
- 宣言時初期化を行っているローカル変数でのみ利用可能。
- 宣言時初期化のあとにその変数の値を変更するなどしている場合、コードの意味・実行結果が変わる場合がある。

![LocalVariableToInline_Error.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426590/068f51d6-bc0d-68b8-2f54-726415e7b19d.png)

## メソッドの抽出
コード内の一部分を新規メソッドとして抽出できる。
![ExtractMethod.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426590/9baccfcd-2435-4004-8610-b41dfb45afac.gif)

1. メソッド化したい範囲を選択する。
1. [クイック アクションとリファクタリング] メニューを表示し、[メソッドの抽出]を選ぶ。<br />または、`Ctrl+R, Ctrl+M`を押す。
1. 現在のメソッドの直下に新しいプライベートメソッドが作成される。自動的に名前変更モードになるので、適切な名前を入力し`Enter`キーで確定する。

### 注意事項
- コードの複雑さによっては抽出処理に時間がかかったり、思うような結果が得られない場合がある。

## メソッドのインライン化
メソッドに分けるほどでもない処理を呼び出し側に直接記述するようにし、不要になったメソッドを削除する。
![MethodToInline.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426590/85913bfa-407a-e2cf-9dd1-eb3090ee5b4a.gif)

1. インライン化したいメソッドの呼び出し部分にカーソルを置く。
1. [クイック アクションとリファクタリング] メニューを表示し、[インライン '（メソッド名）']をクリックする。

### オプション
- [インラインおよび '（メソッド名）' の保持]：メソッドのインライン化を行うが、元のメソッドは削除せずそのまま保持する。

### 注意事項
- インライン化するメソッドの処理内容が1行のときのみ利用可能。
    - `Console.WriteLine()` ×2行にするとインライン化できなくなった。

## フィールドのカプセル化（フィールドからプロパティ生成）
`public`や`internal`/`Friend`などで直接外部に公開しているフィールドを`private`にし、代わりにプロパティを介して公開するように変更できる。
![Capsule.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426590/ec92032a-70c7-e12b-7fd1-2e13d34b45a4.gif)
![CapsuleVB.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426590/d7ea45e8-9034-abd6-cd15-b8af8d3d0d16.gif)

1. カプセル化したいフィールドの宣言部にカーソルを置く。
1. [クイック アクションとリファクタリング] メニューを表示し、[フィールドのカプセル化: '（フィールド名）' (およびプロパティを使用します)]を選択する。
1. 当該フィールドが`private`に変更される。また、`public`や`internal`/`Friend`のプロパティが生成され、当該フィールドを参照するようになる。

## 完全なプロパティと自動実装プロパティの相互変換
完全なプロパティ（get/setを手動で書く）と、自動実装プロパティとを相互に変換できる。
![AutoProperty.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426590/9c4e5cbc-88fc-3fb9-82c2-ca96d0f14342.gif)
![AutoPropertyVB.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426590/40d3a32c-be0b-72e6-9a43-6693bdc8d2bd.gif)

1. 変換したいメソッドの宣言部にカーソルを置く。
1. [クイック アクションとリファクタリング] メニューを表示し、[自動プロパティに変換する[^1]]（翻訳がおかしいが、自動→完全への変換）、[自動プロパティを使用する]（完全→自動）
1. 当該フィールドが`private`に変更される。また、`public`や`internal`/`Friend`のプロパティが生成され、当該フィールドを参照するようになる。

[^1]: 英語版では "Convert to full property" となっている。

## その他のリファクタリング機能

https://docs.microsoft.com/ja-jp/visualstudio/ide/refactoring-in-visual-studio?view=vs-2019

# 便利な機能・設定
## パラメーターヒント
`Ctrl+Shift+Space` でパラメーターのヒント（パラメーター名、型、あれば説明）を表示できる。
メソッド名の後に`(`を入力すると自動的に出てくるが、別の場所にカーソルを移してヒントを消してしまったときや、すでに引数を設定しているけれど別のオーバーロードを確認したいといった場合に便利。
![ParamHint.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426590/6ef0c3d8-5ea2-1bf0-4423-c970af24bbb2.gif)

1. パラメーターを確認したいメソッドの`()`内にカーソルを置く。
1. `Ctrl+Shift+Space`を押すと、パラメーターのヒントが表示される。
1. 上下キーで別のオーバーロードを確認できる。

## インラインヒント
`Alt+F1`を押し続けている間、すでに記述されているメソッドの引数名（C#では `var` キーワードにより推論されている型も）をインライン表示できる。
![InlineHint.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426590/e23d79ef-bc74-61ab-0488-fd4df819d0ee.gif)

この機能を利用するためには以下の設定を有効にする必要がある。
> [ツール]→[オプション]→[テキスト エディター]→[C#]または[Basic]（VB）→[詳細]→[Alt+F1を押しながらすべてのヒントを表示する]

![InlineHintSettingsCs.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426590/eb4dbfa4-d76e-8146-1cda-a715d66ac3ac.png)
![InlineHintSettingsVB.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426590/99719eab-b676-44a3-4024-e438f180c41c.png)

## スクロールマップ
スクロールバーの表示をマップモードにすると、コードの縮小表示を確認できる。
また、マウスホバー時に該当部分のプレビューを表示できる。
![ScrollMap.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426590/0faa6ed3-84b6-3f3d-ca91-3503c6228386.gif)