93
100

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Visual Studio 2019 コードクリーンアップの動作をC#サンプル付きで紹介する

Last updated at Posted at 2020-06-22

(2022/9/16更新)
Microsoft公式サイトに、コードクリーンアップと .editorconfig 設定の対応表が掲載されました。
Visual Studio 2022で追加された設定項目にも対応しており、各設定項目ごとのコードサンプルが記載されているので、そちらのページも合わせてご参照ください。

Visual Studio 2019にはコードクリーンアップという機能が付いています。これはあらかじめ設定しておいたルールに従って、ソースコードをまとめて綺麗にしてくれるというものです。
(従来からあった「フォーマット」機能とは異なり、変数宣言にvarを使うか使わないか、自分自身のメソッド呼び出しにthisを付ける/付けないなど、コードの内容部分まで含めて綺麗にしてくれます)

WS000831.PNG

WS000833.PNG

これはとても便利な機能なのですが、惜しいことにどのルールが具体的にどんな処理をしてくれるのかが分かりにくいという欠点があります。
ルールの設定画面では処理の実例が表示されず、各ルールについての説明なども特にないためです。

そこでこの記事では、各ルールがどのような処理を行ってくれるのかを、C#のコードサンプルと合わせて紹介します。
また、実際に行われるクリーンアップ処理の内容は、Visual Studioのコードスタイル設定や .editorconfig の設定によって変わります。そのため、「Visual Studioや .editorconfig のどの設定がクリーンアップ処理に影響するか」も併せて記載します。

1. 'this.' 修飾の基本設定を適用します

自分自身が持つプロパティ、メソッド呼び出しの前に this. を付けるかどうかを統一します。

クリーンアップ前
public class Class1
{
    private string FirstName { get; set; }
    private string FamilyName { get; set; }

    public virtual string GetNameWithSuffix(string suffix)
    {
        return this.FirstName + suffix;
    }

    public override string ToString()
    {
        return this.GetNameWithSuffix("さん");
    }
}
クリーンアップ後
public class Class1
{
    private string FirstName { get; set; }
    private string FamilyName { get; set; }

    public virtual string GetNameWithSuffix(string suffix)
    {
        return FirstName + suffix; /* this. なしに統一 */
    }

    public override string ToString()
    {
        return GetNameWithSuffix("さん"); /* this. なしに統一 */
    }
}

Visual Studio 2019で対応する設定:
オプション > テキスト エディター > C# > コードスタイル > 全般 > 'this' の優先 > ○○ アクセスを 'this' で修飾してください

WS000818.PNG

editorconfigで対応する設定:
dotnet_style_qualification_for_***

# this. と Me. の設定
dotnet_style_qualification_for_event = false:silent
dotnet_style_qualification_for_field = false:silent
dotnet_style_qualification_for_method = false:silent
dotnet_style_qualification_for_property = false:silent

参考ページ:
"This." 修飾子 と "Me." 修飾子 (EditorConfig での .NET の言語規則 - Visual Studio)

2. using を並べ替える

usingを適切な順番に並べ替えます。

クリーンアップ前
using System;
using Newtonsoft.Json;
using System.Linq;
using System.Text;
using Newtonsoft.Json.Converters;
using System.IO;
using Semver;
クリーンアップ後
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Semver;
using System;
using System.IO;
using System.Linq;
using System.Text;

なお、Visual Studio 2019の標準では上記のようにアルファベット順に並べ替えますが、設定を変更することでSystemを先頭に並び替えたり、グループごと(名前空間の第1階層ごと)に空行を空けて並べたりすることも可能になります。

using System;
using System.IO;
using System.Linq;
using System.Text;

using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

using Semver;

Visual Studio 2019で対応する設定:
オプション > テキスト エディター > C# > 詳細 > ディレクティブを使用する

WS000819.PNG

editorconfigで対応する設定:
dotnet_separate_import_directive_groups, dotnet_sort_system_directives_first

# using の整理
dotnet_separate_import_directive_groups = false
dotnet_sort_system_directives_first = false
file_header_template = unset

参考ページ:
using ディレクティブの整理 (EditorConfig での .NET の書式規則)

3. 不要な using の削除

ファイル内で使用していないusing句を削除します。

クリーンアップ前
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Semver;
using System;
using System.IO;
using System.Linq;
using System.Text;

namespace CodeCleanupDemo
{
    public class TestClass1
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            File.WriteAllText("world.log", "Hello World!");
        }
    }
}
クリーンアップ後
using System;
using System.IO;

namespace CodeCleanupDemo
{
    public class TestClass1
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            File.WriteAllText("world.log", "Hello World!");
        }
    }
}

Visual Studio 2019で対応する設定:
なし

editorconfigで対応する設定:
なし

4. 暗黙的/明示的な型の基本設定を適用します

ローカル変数の宣言時に var を使うかどうかを統一します。

クリーンアップ前
var num1 = 100;
var str1 = "foo";
クリーンアップ後
int num1 = 100;
string str1 = "foo";

Visual Studio 2019で対応する設定:
オプション > テキスト エディター > C# > コードスタイル > 全般 > 'var' を優先

WS000825.PNG

editorconfigで対応する設定:
csharp_style_var_***

# var を優先
csharp_style_var_elsewhere = false:silent
csharp_style_var_for_built_in_types = false:silent
csharp_style_var_when_type_is_apparent = false:silent

参考ページ:
'var' の設定 (IDE0007 および IDE0008))

5. 未使用の変数を削除する

使われていないローカル変数を削除します。

クリーンアップ前
public static void Main(string[] args)
{
    var num = 100;

    Console.WriteLine("Hello World!");
}
クリーンアップ後
public static void Main(string[] args)
{
    Console.WriteLine("Hello World!");
}

Visual Studio 2019で対応する設定:
なし

editorconfigで対応する設定:
なし

参考ページ:
コンパイラの警告 (レベル 3) CS0219

6. 不要なキャストを削除する

不要と考えられる型キャストを削除します。

クリーンアップ前
public static void Resize(Rectangle baseRect, int requiredWidth, int requiredHeight)
{
    var left = (int)baseRect.X;

    var scaleRateByWidth = (double)baseRect.Width / (double)requiredWidth;
    var scaleRateByHeight = (double)baseRect.Height / (double)requiredHeight;
クリーンアップ後
public static void Resize(Rectangle baseRect, int requiredWidth, int requiredHeight)
{
    var left = baseRect.X;  // Rectangle.Xはもともとint型のため、int型へのキャストは不要

    var scaleRateByWidth = baseRect.Width / (double)requiredWidth; // requiredWidthだけdouble型にキャストしても結果は同じ
    var scaleRateByHeight = baseRect.Height / (double)requiredHeight; // 同上

Visual Studio 2019で対応する設定:
なし

editorconfigで対応する設定:
なし

参考ページ:
不要なキャストを削除する (共通のクイックアクション - Visual Studio)

7. インラインの 'out' 変数の基本設定を適用します

関数呼び出し時にout引数を指定していて、かつout引数で使う変数をそれよりも前に宣言している場合、C# 7.0から導入された書き方(out変数)に変更します。

クリーンアップ前
public void TestMethod1()
{
    /* C# 6.0 / Visual Studio 2015以前では、out引数に使うための変数を、事前に宣言しておく必要があった */
    int a;    
    TestMethod2(out a);

    var a3x = a * 3;
} 

public void TestMethod2(out int outParam1)
{
    outParam1 = 100;
}
クリーンアップ後
public void TestMethod1()
{
    /* C# 7.0 / Visual Studio 2017以降ではこのように1行で書ける */
    TestMethod2(out int a);    

    var a3x = a * 3;
}

public void TestMethod2(out int outParam1)
{
    outParam1 = 100;
}

Visual Studio 2019で対応する設定:
オプション > テキスト エディター > C# > コードスタイル > 変数の優先順位 > インライン変数宣言を優先する

WS000826.PNG

editorconfigで対応する設定:
csharp_style_inlined_variable_declaration

# 式レベルの設定
csharp_style_inlined_variable_declaration = true:suggestion

参考ページ:
インライン変数宣言 (IDE0018))

8. アクセシビリティ修飾子を追加します

アクセシビリティ (public, protected, privateなど) の記載がされていないプロパティやメソッドについて、アクセシビリティを記載します。

クリーンアップ前
double Rate { get; set; }  // アクセシビリティは指定していないがprivate扱い

static void Resize(Rectangle baseRect, int requiredWidth, int requiredHeight)  // 同上
{
}
クリーンアップ後
private double Rate { get; set; }

private static void Resize(Rectangle baseRect, int requiredWidth, int requiredHeight)
{
}

Visual Studio 2019で対応する設定:
なし

editorconfigで対応する設定:
なし

9. アクセシビリティ修飾子を並べ替える

アクセシビリティ (public, protected, privateなど) の記載順を、標準の並び順に従って並び替えます。
また、static修飾子やvirtual修飾子などよりも前に(先頭に)来るようにします。

クリーンアップ前
static private void Resize(Rectangle baseRect, int requiredWidth, int requiredHeight)
{
}

internal protected void Resize(Rectangle baseRect)
{
}
クリーンアップ後
private static void Resize(Rectangle baseRect, int requiredWidth, int requiredHeight)
{
}

protected internal void Resize(Rectangle baseRect)
{
}

Visual Studio 2019で対応する設定:
なし

editorconfigで対応する設定:
なし

10. 可能な場合、privateフィールドを読み取り専用にする

初期化以外の箇所で代入を行っていないprivateフィールド(メンバ変数)があれば、それをreadonlyにします。

※readonlyになるのはフィールドだけで、プロパティは読み取り専用にならないことに注意してください。

クリーンアップ前
public class TestClass1
{
    private string _world = "World";

    public virtual void HelloWorld()
    {
        Console.WriteLine($"Hello, {_world}");
    }
}
クリーンアップ後
public class TestClass1
{
    /* 初期化以外で代入(再設定)を行っていないため、readonlyになる */
    private readonly string _world = "World"; 

    public virtual void HelloWorld()
    {
        Console.WriteLine($"Hello, {_world}");
    }
}

Visual Studio 2019で対応する設定:
オプション > テキスト エディター > C# > コードスタイル > 修飾子設定 > readonly フィールドを優先する

WS000827.PNG

editorconfigで対応する設定:
dotnet_style_readonly_field

# フィールド設定
dotnet_style_readonly_field = true:suggestion

参考ページ:
読み取り専用修飾子を追加する (IDE0044)

11. 言語/フレームワークの型の基本設定を適用します。

標準の組み込み型名について、言語キーワード(int, long, stringなど)で記述するか、フレームワークの型名 (Int32, Int64, System.Stringなど) で記述するかを統一します。

クリーンアップ前
Int32 num1 = 100;
System.String str1 = "foo";

var num2 = Int64.MaxValue;
クリーンアップ後
int num1 = 100;
string str1 = "foo";

var num2 = long.MaxValue;

Visual Studio 2019で対応する設定:
オプション > テキスト エディター > C# > コードスタイル > 定義済みの型の設定

WS000829.PNG

editorconfigで対応する設定:
dotnet_style_predefined_type_for_***

# 言語キーワードと BCL の種類の設定
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
dotnet_style_predefined_type_for_member_access = true:silent

参考ページ:
型参照のためのフレームワーク型名の代わりに言語キーワードを使用する (IDE0049)

12. 単一行のコントロール ステートメントに対する波かっこの追加/削除を行います

if文やusing文などの本体を1行だけで書けるような場合に、波かっこをつけて複数行で記述するか、1行で記述するかを統一します。

**(2020/7/3追記)**Visual Studio 2019 16.7.0 Preview 2時点では、波かっこの追加は行いますが、削除は行ってくれないようです。(バグ?)

クリーンアップ前
public virtual string GetNameWithSuffix(string name, string suffix)
{
    if (name == null) return null;
クリーンアップ後
public virtual string GetNameWithSuffix(string name, string suffix)
{
    if (name == null)
    {
        return null;
    }

なお、この設定には「はい」「いいえ」の他に「複数行の場合」という設定があり、これを選択すると「本体が1行に収まるときは1行で書く、複数行にわたるときは波かっこをつける」という動作となります。

クリーンアップ前
public virtual string GetNameWithSuffix(string name, string suffix)
{
    if (name == null) return null;

    if (suffix != null)
        return string.Format("こんにちは、{0} {1}",
                             name,
                             suffix);
クリーンアップ後
public virtual string GetNameWithSuffix(string name, string suffix)
{
    /* 本体が1行なのでそのまま */
    if (name == null) return null;

    /* 本体が複数行なので波かっこが付く */
    if (suffix != null)
    {
        return string.Format("こんにちは、{0} {1}",
                        name,
                        suffix);
    }

Visual Studio 2019で対応する設定:
オプション > テキスト エディター > C# > コードスタイル > コード ブロックの優先順位 > 波かっこを優先します

WS000830.PNG

editorconfigで対応する設定:
csharp_prefer_braces

# コード ブロックの設定
csharp_prefer_braces = true:silent

参考ページ:
中かっこを追加する (IDE0011)

13. オブジェクト/コレクションの初期化の基本設定を適用します

オブジェクトやコレクションを初期化する時の書き方を統一します。

クリーンアップ前
public class Human
{
    public virtual string Name { get; set; }
    public virtual int Height { get; set; }

    public static Human Create()
    {
        var human1 = new Human();
        human1.Name = "Taro";
        human1.Height = 160 * 1000;

        var specials = new List<string>();
        specials.Add("UltraThrow");
        specials.Add("TarouBarriar");

        var families = new Dictionary<string, string>();
        families["mother"] = "Mother of Ultra";
        families["brother"] = "Ace";

        return human1;
    }
}
クリーンアップ後
public class Human
{
    public virtual string Name { get; set; }
    public virtual int Height { get; set; }

    public static Human Create()
    {
        /* オブジェクト初期化子を使った書き方に変更 */
        var human1 = new Human   
        {
            Name = "Taro",
            Height = 160 * 1000
        };

        /* コレクション初期化子を使った書き方に変更 */
        var specials = new List<string>   
        {
            "UltraThrow",
            "TarouBarriar"
        };

        /* コレクション初期化子を使った書き方に変更
           (C# 6.0 / Visual Studio 2015以降でのみ可能な書き方) */
        var families = new Dictionary<string, string>   
        {
            ["mother"] = "Mother of Ultra",
            ["brother"] = "Ace"
        };

        return human1;
    }
}

Visual Studio 2019で対応する設定:
オプション > テキスト エディター > C# > コードスタイル > 全般 > 式の優先順位
オブジェクト初期化子を優先する
コレクション初期化子を優先する

WS000811.PNG

editorconfigで対応する設定:
dotnet_style_collection_initializer, dotnet_style_object_initializer

# 式レベルの設定
dotnet_style_collection_initializer = true:suggestion
dotnet_style_object_initializer = true:suggestion

参考ページ:
オブジェクト初期化子を使用する (IDE0017)
コレクション初期化子を使用する (IDE0028)

14. 式/ブロック本体の基本設定を適用します

メソッド本体やプロパティなどの本体が1行に収まる場合に、従来の形式で記述するか、式形式で記述するかを統一します。

クリーンアップ前
public class Class1
{
    private string FirstName { get; set; }
    private string FamilyName { get; set; }
    public virtual string Name { get { return string.Format("{0} {1}", FamilyName, FirstName); } }

    private string _nickname = null;
    public virtual string NickName
    {
        get
        {
            return (_nickname ?? Name);
        }
        set
        {
            _nickname = value;
        }
    }
}
クリーンアップ後
public class Class1
{
    private string FirstName { get; set; }
    private string FamilyName { get; set; }
    /* Nameプロパティは式本体を1行で書けるため、式形式に変換される
       (C# 6.0 / Visual Studio 2015以降でのみ可能な書き方) */
    public virtual string Name => string.Format("{0} {1}", FamilyName, FirstName);

    private string _nickname = null;
    public virtual string NickName
    {
        /* get, setの両方を持つプロパティにも対応可能
           (C# 7.0 / Visual Studio 2017以降でのみ可能な書き方) */
        get => (_nickname ?? Name);
        set => _nickname = value;
    }
}

設定によってはコンストラクタ、ローカル関数などプロパティ以外の対象にも適用可能です。
ただし、項目によっては対応するC#のバージョンが異なることに注意してください。
(全項目を使用可能になるのはC# 7.0以降(Visual Studio 2017以降))

Visual Studio 2019で対応する設定:
オプション > テキスト エディター > C# > コードスタイル > 全般 > 式の優先順位 > ○○に式本体を使用する

WS000810.PNG

editorconfigで対応する設定:
csharp_style_expression_bodied_***

# 式のようなメンバー
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = false:silent
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:silent

参考ページ:
式形式のメンバー

補足:ルール選択にかかわらず常に実行される処理

下記の処理は、どのようなルールを選択したかにかかわらず、クリーンアップ時は常に実行されます。

  • コードのフォーマット(メニューの 編集 > 詳細 > ドキュメントのフォーマット と同じ)

補足:そのほか、知っておくと便利な知識

  • コードクリーンアップのための設定内容は、Visual Studioの「設定から .editorconfig を生成」機能を使うことで、ほかの人と共有することができます。
    (メニューの オプション > テキスト エディター > C# > コード スタイル から実行可能です)

    WS000837.PNG

    ただし、残念ながら 「どのクリーンアップルールを選択したか」の情報は共有できない ため、これだけは別途共有する必要があります。
    (例:「不要な using の削除」をオンにしているかオフにしているかの情報は .editorconfig には含まれないため、各開発者がコードクリーンアップの設定ダイアログからオン/オフを切り替えなくてはなりません)

  • Microsoftの公式ドキュメントでも紹介されている拡張機能「Code Cleanup On Save」を使うと、ファイルを保存するたびに自動でクリーンアップが実行されるようにできます。

    WS000835.PNG

    WS000834.PNG

93
100
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
93
100

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?