1.

C# の基本的な書き方|命名規則・var・null 許容参照型・トップレベル

編集
この記事の要点
  • 文の終わりは ;、ブロックは { }大文字小文字を区別する
  • メソッドとプロパティは PascalCase(先頭が大文字)。Java や JavaScript と違う点
  • ローカル変数は初期化しないと使えない。var型推論で、動的型付けではない
  • .NET 6 以降は トップレベルステートメントMain を書かずに済む
  • 整形は dotnet format、規約は .editorconfig に書いてツールに任せる

いちばん短いプログラム

// .NET 6 以降: トップレベルステートメント
Console.WriteLine("こんにちは");
// 従来の書き方(今も有効)
using System;

namespace MyApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("こんにちは");
        }
    }
}

トップレベルステートメントは1 つのプロジェクトに 1 ファイルだけ書けます。コンパイラが裏で Main を生成しています。

名前空間と using

// ファイルスコープ名前空間(C# 10 以降・インデントが 1 段減る)
namespace MyApp.Models;

using System;
using System.Collections.Generic;
using System.Linq;

using MyList = System.Collections.Generic.List<string>;   // 別名を付ける
global using System.Text;                                    // 全ファイルに適用(C# 10)

public class User { }

.NET 6 以降のプロジェクトでは 暗黙的な usingImplicitUsings)が既定で有効なため、SystemSystem.Linq を書かなくても使えます。

命名規則

対象書き方
クラス・構造体・列挙型PascalCaseUserProfile
メソッドPascalCaseGetTotal()
プロパティPascalCaseUserName
public フィールド・定数PascalCaseMaxRetry
ローカル変数・引数camelCaseuserName
private フィールド_camelCase_userName
インターフェースI + PascalCaseIUserService
非同期メソッド末尾に AsyncLoadAsync()

メソッド名が大文字で始まるのが C# の最大の特徴です。Java や JavaScript の getTotal() は、C# では GetTotal() になります。

public class User
{
    private readonly string _name;          // private フィールド
    public const int MaxLength = 20;        // 定数

    public string Name { get; set; }        // 自動プロパティ
    public string DisplayName => $"{Name} さん";   // 式形式プロパティ

    public User(string name) => _name = name;

    public int GetLength() => _name.Length;
}

変数と型

int count = 10;
string name = "田中";
double price = 1980.5;
bool isActive = true;

var list = new List<string>();      // 型推論(右辺から決まる)
var n = 10;                          // int に確定する

// var は動的型付けではない
// n = "十";                          // コンパイルエラー

// 本当に動的にしたいときだけ dynamic(実行時まで解決されない)
dynamic d = 10;
d = "十";                             // 通る

int m;
// Console.WriteLine(m);              // エラー: 未割り当てのローカル変数

const int Max = 100;                 // コンパイル時定数
readonly DateTime _created = DateTime.Now;   // 実行時に 1 度だけ

null 許容参照型

#nullable enable

string name = null;        // 警告: null リテラルを非 null 参照に変換できません
string? maybe = null;      // OK: null を許す型

int len = maybe.Length;    // 警告: null の可能性がある
int len2 = maybe?.Length ?? 0;    // 安全

string value = maybe ?? "既定値";
maybe ??= "既定値";        // null のときだけ代入

int? num = null;           // 値型を null 許容にする
if (num.HasValue) { }

.NET 6 以降のテンプレートでは <Nullable>enable</Nullable> が既定で有効です。? の有無で「null が入り得るか」を型として表せるため、NullReferenceException を大幅に減らせます。

文字列

var name = "田中";
var count = 3;

Console.WriteLine($"{name} さんの件数は {count} 件");     // 文字列補間
Console.WriteLine($"{count,5}");                        // 右寄せ 5 桁
Console.WriteLine($"{price:N0} 円");                     // 3 桁区切り
Console.WriteLine($"{DateTime.Now:yyyy/MM/dd}");

var path = @"C:\Users\taro";           // 逐語的文字列(エスケープ不要)
var json = """
    { "name": "田中" }
    """;                                // 生文字列リテラル(C# 11)

// 空判定
string.IsNullOrEmpty(s);               // null または ""
string.IsNullOrWhiteSpace(s);          // null または空白だけ

// 連結を繰り返すなら StringBuilder
var sb = new StringBuilder();
foreach (var x in items) sb.Append(x);

コメントと XML ドキュメント

// 1 行コメント

/*
   複数行コメント
*/

/// <summary>
/// 税込価格を計算します。
/// </summary>
/// <param name="price">税抜価格</param>
/// <returns>税込価格</returns>
/// <exception cref="ArgumentOutOfRangeException">price が負のとき</exception>
public int TaxIncluded(int price) => (int)(price * 1.1);

/// で書いた説明はVisual Studio の補完に表示され、ビルド時に XML ファイルとしても出力できます。

整形と規約

# .editorconfig
root = true

[*.cs]
indent_style = space
indent_size = 4
end_of_line = crlf
charset = utf-8
dotnet_sort_system_directives_first = true
csharp_new_line_before_open_brace = all
dotnet_naming_rule.private_fields_underscore.severity = warning
dotnet format                # .editorconfig に従って整形する
dotnet build -warnaserror    # 警告をエラー扱いにする

C# では波かっこを次の行に置く(Allman スタイル)のが公式のガイドラインです。Java や JavaScript と違う点なので、他言語から来たときは .editorconfig をチームで共有してください。

制御構文

if (score >= 80)
{
    Console.WriteLine("A");
}
else if (score >= 60)
{
    Console.WriteLine("B");
}
else
{
    Console.WriteLine("C");
}

// switch 式(C# 8 以降)
var grade = score switch
{
    >= 80 => "A",
    >= 60 => "B",
    _ => "C",
};

foreach (var item in items)
{
    if (item is null) continue;
    Console.WriteLine(item);
}

for (int i = 0; i < 3; i++) { }

while (n > 0) { n--; }

// LINQ で書ける処理はループにしない
var names = users.Where(u => u.IsActive)
                 .OrderBy(u => u.Name)
                 .Select(u => u.Name)
                 .ToList();

C# の switchフォールスルーを許しませんbreak(または return goto)を書かないとコンパイルエラーになるため、書き忘れによるバグが起きません。

例外処理と using

try
{
    var text = File.ReadAllText(path);
}
catch (FileNotFoundException ex)
{
    Console.Error.WriteLine($"ファイルがありません: {ex.FileName}");
    throw;                       // そのまま投げ直す(throw ex; は履歴が消える)
}
catch (IOException ex) when (ex.HResult == -2147024864)   // 条件付き catch
{
    Console.Error.WriteLine("他のプロセスが使用中です");
}
finally
{
    Console.WriteLine("必ず実行される");
}
// using: スコープを抜けるときに自動で Dispose される
using (var stream = File.OpenRead(path))
{
    // ...
}

// using 宣言(C# 8 以降)。ブロックのネストが減る
using var stream = File.OpenRead(path);
// メソッドを抜けるときに Dispose される

throw ex; と書くとスタックトレースが上書きされ、どこで起きたか分からなくなります。そのまま投げ直すときは throw; だけを書いてください。

非同期処理

public async Task<string> LoadAsync(string url)
{
    using var client = new HttpClient();
    var response = await client.GetAsync(url);
    response.EnsureSuccessStatusCode();
    return await response.Content.ReadAsStringAsync();
}

// 呼ぶ側も await する
var text = await LoadAsync(url);

// やってはいけない: デッドロックの原因になる
// var text = LoadAsync(url).Result;
// LoadAsync(url).Wait();

async メソッドの戻り値は TaskTask<T> にします。async void はイベントハンドラー以外で使わないでください。例外を受け取れず、アプリが落ちます。

よくあるつまずき

症状原因
CS0103: 名前 'X' は現在のコンテキストに存在しません綴り違い(大文字小文字を区別する)/using の不足
CS0165: 未割り当てのローカル変数初期化していない。= null= default を入れる
CS0161: すべてのコード パスが値を返すわけではありませんreturn の無い経路がある
NullReferenceException#nullable enable にして警告を潰す
CS0246: 型または名前空間名が見つかりませんNuGet パッケージ未追加/プロジェクト参照の不足
実行できないエントリポイント(Main)が無い/複数ある

関連

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 基本的なルール
  2. 継承

最近更新/作成されたページ