.editorconfigは様々な言語でコードの設定を統一するための設定ファイルです。C#も.editorconfigを使うとコードフォーマットやアナライザーの設定できます。今回は私がこれまでC#で.editorconfigを使ってきておすすめ設定と避けたほうがいいと考えている設定を紹介します。
おすすめ設定
リポジトリルートに配置するベース設定としての.editorconfigを示します。charset
とend_of_line
はOSや言語によって考慮が変わるため、その部分は適宜変更する必要があるため後段で補足説明します。
なお、MicrosoftのC#のコード規約に合わせたルール設定は含んでいませんが、これも.editorconfigで明示できますし個人のVS/Rider設定に依存しないのでオススメです。
root = true # target to global files like .yaml, .json, .md, .gitignore and .gitattributes [*] # Change these settings to your own preference indent_style = space indent_size = 2 # We recommend you to keep these unchanged end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.cs] indent_style = space indent_size = 4 # Depends on your OS end_of_line = unset # if you want to use git's default # end_of_line = crlf # NOT RECOMEND, If you checkout on Windows # end_of_line = lf # NOT RECOMEND, if you never checkout on Windows # Depends on your language charset = utf-8-bom # If you use Japanese, use utf-8-bom to avoid garbage character on git by shift-jis # charset = utf-8 # If you use English only like OSS project [*.{csproj,props}] indent_style = space indent_size = 2 [Dockerfile] indent_style = space indent_size = 4 [{appsettings.json,appsettings.*.json}] indent_style = space indent_size = 2 [*.cs] # C# Standards dotnet_diagnostic.CS1998.severity = none # This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. # Nullable Reference Types dotnet_diagnostic.CS8618.severity = error # Non-nullable field is uninitialized. Consider declaring as nullable. dotnet_diagnostic.CS8604.severity = error # Possible null reference argument. dotnet_diagnostic.CS8629.severity = error # Nullable value type may be null. dotnet_diagnostic.CS8600.severity = error # Converting null literal or possible null value to non-nullable type. dotnet_diagnostic.CS8603.severity = error # Possible null reference return. dotnet_diagnostic.CS8610.severity = error # Nullability of reference types in type of parameter doesn't match overridden member. dotnet_diagnostic.CS8625.severity = error # Cannot convert null literal to non-nullable reference type. dotnet_diagnostic.CS8606.severity = error # Possible null reference assignment to iteration variable dotnet_diagnostic.CS8602.severity = error # Dereference of a possibly null reference. dotnet_diagnostic.CS8601.severity = error # Possible null reference assignment. dotnet_diagnostic.CS8614.severity = error # Nullability of reference types in type of parameter doesn't match implicitly implemented member. dotnet_diagnostic.CS8765.severity = error # Nullability of type of parameter 'obj' doesn't match overridden member (possibly because of nullability attributes). dotnet_diagnostic.CS8619.severity = error # Nullability of reference types in value of type 'GenericType<T?>' doesn't match target type 'GenericType<T>'. # ================= # Analyzer Rulesets # ================= # Microsoft.CodeAnalysis.BannedApiAnalyzers dotnet_diagnostic.RS0030.severity = error # RS0030: Banned API # Microsoft.VisualStudio.Threading.Analyzers dotnet_diagnostic.VSTHRD100.severity = error # VSTHRD100: Avoid async void methods dotnet_diagnostic.VSTHRD101.severity = error # VSTHRD101: Avoid unsupported async delegates dotnet_diagnostic.VSTHRD110.severity = error # VSTHRD110: Observe result of async calls dotnet_diagnostic.VSTHRD003.severity = none # VSTHRD003 Avoid awaiting foreign Tasks # ================= # Format Rulesets # ================= # IDE0160: Convert to file-scoped namespace csharp_style_namespace_declarations = file_scoped:warning # Microsoft.Analyzers.ManagedCodeAnalysis dotnet_diagnostic.CA2200.severity = error # Rethrow to preserve stack details # ================= # Too Match Detail Rulesets # ================= # CONSIDER: Are IDE0051 and IDE0052 too noisy to be warnings for IDE editing scenarios? Should they be made build-only warnings? dotnet_diagnostic.IDE0005.severity = warning # Remove unnecessary imports dotnet_diagnostic.IDE0051.severity = warning # Remove unused private member dotnet_diagnostic.IDE0052.severity = warning # Remove unread private member dotnet_diagnostic.IDE0079.severity = none # Remove unnecessary suppression dotnet_diagnostic.IDE0090.severity = none # Simplify new expression
考慮が必要な設定
対象のファイル
.editorconfigはファイル拡張子やファイル名をターゲットとして指定するため、感覚としては.gitattributesに似ています。ということは注意点も似ているということです。例えば、*.cs
はC#のソースコードを指定しますが、*.cs
はC#のプロジェクトファイルではありません。このため、*.csproj
や*.props
も指定する必要があります。
# C#のソースコードが対象 [*.cs] # C#のプロジェクトファイルが対象 [*.{csproj,props}]
また、*
は全ファイルを対象にする非常に強力な指定です。どのファイルでも原則とする設定を差し込んておいて、調整したいファイル拡張子に対して別途指定することでオーバーライドすると設定がスリムになります。例えば上記.editorconfigでは、原則改行コードはlf
、charsetをutf-8
としています。その後、yamlやjson、csファイルを対象にインデントやcharsetを調整しています。
# 全ファイルが対象 [*] indent_style = space indent_size = 2
OSを考慮したeol設定
残念ながら.editorconfigはOSの違いを判別する機能を持たないため、OSごとに設定を変えることはできません。1このため、OSを考慮すると「git config
のautocrlfに任せて無指定かunset
」がおすすめです。もし利用するOSを固定できるなら、WindowsかLinux/macOSでend_of_lineを選択するのもいいでしょうが、あまり指定するメリットはないと考えます。避けたい設定で、Windows環境でlf
を指定するとおこる弊害を説明します。
[*.cs] end_of_line = unset # gitのautocrlfに任せるのがおすすめ
OS | end_of_line | 備考 |
---|---|---|
両OSで利用する場合 | unset |
git のリモート、ローカルEOLに任せる |
Windows | crlf |
eolで悩まないためにgit config core.autocrlf=true を前提とする |
Linux/macOS | lf |
Windowsではないのでlfになることが期待される |
言語を考慮したcharset設定
日本語を用いるプロジェクトと英語のみを用いるプロジェクトでcharsetの考慮が変わります。Visual Studioは日本語を含むとshift-jisでファイル保存しようとするのですがgit
でshift-jisは文字化けを起こしトラブルの原因となります。このため、コード上で日本語を用いるプロジェクトではcharset=utf-8-bom
を用いてGitでも日本語表示できるようにしつつ、ローカルでも日本語を文字化けさせないのがオススメです。
[*.cs] charset = utf-8-bom # 日本語を含む場合、utf-8-bomにするとgitで文字化けを防ぎつつVisual Studioでも日本語を表示できる
一方、英語(ascii)のみを用いるプロジェクトではcharset=utf-8
を用いて良いでしょう。ただ、Roslynチームはcharset=utf-8-bom
を用いていることから、utf-8-bom
が古臭い以外に使わない積極的な理由もないと認識しています。2
[*.cs] charset = utf-8 # 英語だけならutf-8で十分
言語 | charset | 備考 |
---|---|---|
日本語を用いるプロジェクト | utf-8-bom |
Gitで日本語を表示し、ローカルでも日本語を文字化けさせない |
英語のみを用いるプジェクト | utf-8 |
一般的な設定 |
アナライザー設定
C#のアナライザー設定も.editorconfigで設定できます。アナライザーごとの警告レベルを調整できるので、プロジェクトで統一したアナライザー設定を展開するときに利用するといいでしょう。例えば、Nullable Reference Typesを導入したプロジェクトでは、dotnet_diagnostic.CS8618.severity = error
を設定することで、警告をエラーに昇格させることができます。nullableを必須にできるので、プロジェクトの初期から有効にするのがオススメです。設定可能な値はConfiguration options for code analysis | Mirosoft Learnで公開されています。
[*.cs] dotnet_diagnostic.xxxxx.severity = error|warning|info|none
Microsoft LearnにEditorConfigをVisual Studioで利用する方法が紹介されています。ファイルの配置についても詳しく説明されているので、これまで知らなかった人は参考にしてください。
C#のコード規約
C#のコード規約も.editorconfigで設定できます。多くのプロジェクトで採用されているであろう「MicrosoftのC#コード規約」も.editorconfigで設定することで、プロジェクト全体でコード規約を統一できます。設定可能な値は、Code style rule options | Mirosoft Learnで公開されています。例えばdotnet_style_qualification_for_method = false
で「this.
を省略させたい」意図がわかります。
[*.cs] # this. and Me. preferences dotnet_style_qualification_for_event = false dotnet_style_qualification_for_field = false dotnet_style_qualification_for_method = false dotnet_style_qualification_for_property = false
Visual Studio以外にRiderも.editorconfigを尊重しますし、VS CodeもEditorConfig for VS Codeをインストールすることで.editorconfigを尊重します。.editorconfigを尊重するエディター/IDEを使っていると、プロジェクトにおけるコードフォーマットを統一できていいですね。
避けたい設定
ファイルごとの設定
可能であればファイルごとに.editorconfigでルールを調整するのは避けたほうがいいです。.gitattributesもそうですが、エディターによるファイル管理とこういった設定は連動しません。このため、ファイル指定での設定はファイルのパス変更やファイル名変更しても設定を変え忘れて効かなくなる可能性があります。
想像してください、何かしらの理由でディレクトリを移動させてgit commit
、push、PRマージした後、ほかの人がpullしたときに.editorconfigが効かなくなって、原因が.editorconfigでファイル名やファイルパス指定したからだった時を。私はeditorconfigで設定したことに気づく自信はありませんし、気づくためにエラーを出す仕組みも.editorconfigは持ちません。
# 避ける [foo/bar.json] indent_size = 4 [piyo/buz.json] indent_size = 2 # 可能なら拡張子でまとめる。設定がずれているなら併せられないかまず検討する [*.json] indent_size = 2
Windowsでend_of_lineをlfに指定する
過去数年lf
を試してきたのですが、Windows環境のC#プロジェクトで改行コードをlf
に統一するのは避けた方がいいです。git config core.autocrlf = true
が好ましいという背景込みですが、end_of_line=lf
にしていると「Visual StudioのCRLFとLF混在時のダイアログ」がファイルを開くたびに発生してめんどくさいだけです。
end_of_line
を指定しないということは、C#のRaw String Literalなどファイルの改行コードが文字列改行コードになるもので環境依存が起こります。例えば、テストコードで改行コードを意識しないとWindowsで通るのにLinuxでこけた経験はないでしょうか。このようなケースはString.ReplaceLineEndings
や.Replace("\r\n", "\n")
で文字列の改行コードをLFに統一できます。めんどくさい側面はありますが、利用者の環境でテスト結果が左右するよりコードで解決するほうがいいと考えます。
なお、.editorconfigでlfを設定していてもcrlfが混じる原因はいくつかあるようですが、個人的にはgit config core.autocrlf=true
が原因のことが多く感じます。とはいえ、core.autocrlf=true
はWindowsにおいてOS間の互換性を向上させるために有効な設定なので、これを有効にするのを前提にします。
なぜcore.autocrlfをtrueにするのか
蛇足なので、興味がある人だけ読んでください。
core.autocrlf=trueはWindows専用設定でOS間の互換性を向上させます。
具体的には、クローン/チェックアウト時は「リモートにlfで保存されているファイルをローカルではcrlfに変換」し、リモートプッシュ時は「ローカルのcrlfなファイルをlfに変換」するので、リポジトリ内部は常にlfで統一されます。特にUnityを使っている環境でEOLに伴うGit差分を考慮する必要がなくなるため、git config core.autocrlf=true
はWindowsだけの環境でも有効にしておくのがオススメです。Gitプッシュ時に、EOLによる差分が他の人に影響するかを気にしないで済むのはありがたいです。ただし.sh
などLFが必須なファイルで困るので.gitattributes
で個別設定を併用しましょう。
git config core.autocrlf=false
をオススメする記事やgistをよく見かけますが、falseが引き起こす問題は割と大きいです。例えば、Windowsで誤ってcrlfでプッシュしたファイルをLinuxやmacOSでチェックアウトすると、改行コードがcrlfのままになり、コードの差分が出てしまいます。また、チーム内でOSが混在していたり、人によってcore.autocrlf=true
が混じっていると、不要な差分やマージ時にコンフリクトが発生しえます。プロジェクトで最も大事なのは、リモートリポジトリの安定性なのを考えると、core.autocrlf=true
が最も安定していると考えます。
core.autocrlf=true
でもcore.autocrlf=false
のいずれを使うにしても、チーム内で統一しましょう。
Visual Studioで.editorconfigは調整しない
Visual Studioで.editorconfigファイルを開くと、GUIでdotnetアナライザー設定を調整できます。ただ、.editorconfigに存在しなかったアナライザ設定が差分検出されてしまいます。この差分が1行2行ならいいのですが存在しない設定全てを差分とするので、部分だけ設定している環境では100行近く差し込まれることでしょう。このため、Visual Studioの.editorconfigは閲覧専用にして、設定を変更するときはエディターで行うのがオススメです。
例えば先に示した.editorconfigをVSで開くと次のようなGUIが開き、ファイルの*
から差分が発生したとわかります。
ファイルを保存すると大量の差分が生じています。ちょっとアグレッシブすぎますね。
幸いにしてアナライザーの設定はCode analysis | Microsoft Learnで公開されています。Code style rule optionsやCode quality rulesを見ると、どのような設定・値が可能かがわかります。
まとめ
C#のコード向けに.editorconfigを設定する背景情報をあまり見かけないので、これまで設定競合などを起こしながら着地したおすすめ設定と避けたほうがいい設定をまとめました。コード規約はMicrosoftのC#コード規則とするOSSやプロジェクトが多い印象です。多人数が関わるプロジェクトでは、editorconfigでコード規約を設定すると明解かつ個人のIDE/エディター設定に依存せず、IDEのフルサポートを受けられるのでオススメです。
あと、以前書いた記事のようにdotnet formatのフォーマットも制御できますしね。
参考
- .editorconfig | dotnet/roslyn | GitHub
- .editorconfig | dotnet/runtime | GitHub
- .editorconfig | dotnet/aspnetcore | GitHub
- .editorconfig | dotnet/aspire | GitHub
- 過去にautocrlfサポートが提案されていましたが投票の結果は却下でした。↩
- dotnet/runtimeの.editorconfigは英語で統一されているためかcharsetが指定されていないです。ASP.NETCoreではutf-8が指定されています。↩