🔖

Effective C# 6.0/7.0 メモ - 第 1 章 C# 言語イディオム

2023/08/29に公開

この記事は「Effective C# 6.0/7.0」の読書メモとして、私的プラクティスをまとめています。特に重要だと感じた項目のみ簡潔にまとめています。より詳細な内容に興味のある方は、原著を読んでみることをお勧めします。

項目 1 ローカル変数の型をなるべく暗黙的に指定する

原則 var を使う

自分で型名を宣言するよりも、コンパイラに型推論をしてもらう方がベターです。

// NG
MyClass foo = new MyClass();

// OK
var foo = new MyClass();

ただし、数値型は明示する

数値型は暗黙的な型変換が面倒を起こしうるので、明示しましょう。

// NG: 100.0fと書かないと右辺はdouble型になってしまう
var num = 100.0 / 6;

// OK: floatを明示することでエラーに気づく
float num = 100.0 / 6;

項目 2 const よりも readonly を使用すること

定数は const ではなく static readonly を使う

C#で定数といえば以下の 2 つです。

  • const : コンパイル時定数
  • static readonly : 実行時定数

const の方がパフォーマンスの面では有利ですが、その差はわずかしかありません。柔軟性の面で static readonly の使用を推奨します。

// コンパイル時定数
public const int CONST = 1;

// 実行時定数
public static readonly int STATIC_READONLY = 1;

(と、本には書いてありますが、自分の開発環境では特に不都合がないため const を積極的に使っています。)

項目 3 キャストには is または as を使用すること

as 演算子を使う

むやみにキャストをするよりも as 演算子の方が安全で、かつ実行時の効率も優れています。使用できる場合には常に as を使用しましょう。

// NG: キャストによる型変換が失敗したときには例外が発生する
object o = Factory.GetObject();
try
{
    var t = (MyType)o;
    // 型変換成功
}
catch (InvalidCastException e)
{
    // 型変換失敗
}

// OK: as演算子による型変換が失敗したときにはnullが返される
var t = Factory.GetObject() as MyType;
if (t != null)
{
    // 型変換成功
}
else
{
    // 型変換失敗
}

項目 4 string.Format() を補間文字列に置き換える

変数を含んだ文字列を記述するときは、文字列補間を使う

昔は string.Format() が推奨されていたようですが、現在であれば文字列補間を使いましょう。

// NG: string.Format()による記述
Console.WriteLine(string.Format("私は{0}です", name));

// OK: 文字列補間による記述
Console.WriteLine($"私は{name}です");

項目 6 文字列指定の API を使用しないこと

nameof を使う

クラス名やプロパティ名が必要な時は nameof 演算子を使いましょう。

// NG
Message("MyClass");

// OK
Message(nameof(MyClass));

項目 8 イベントの呼び出し時に null 条件演算子を使用すること

null チェックをしてから実行するパターンでは null 条件演算子を使う

null 条件演算子を使用すると if 文での null チェックに比べコードが簡潔になり、かつスレッドセーフというメリットが得られます。

private Action<int> action;

// NG: ifでnullチェックしてから実行する
if (action != null)
{
    action(0);
}

// OK: null条件演算子で実行する
action?.Invoke(0);

項目 9 ボックス化およびボックス化解除を最小限に抑える

ボックス化とは

“ボックス化”とは object 型やインターフェイス型の引数に、値型の変数を渡すときに暗黙に行われる変換です。その逆の変換のことを”ボックス化解除”といいます。

この、“ボックス化”と”ボックス化解除”を最小限に抑えることで、パフォーマンスが改善されます。

引数の型を明示する

なるべく型を明示するようにしましょう。

public void ObjectWriteLine(object x)
{
    // NG: xにint型が渡された場合、ToString()の呼び出しでボックス化が発生する
    Console.WriteLine(x.ToString());
}

public void IntWriteLine(int x)
{
    // OK: int.ToString()が直接呼ばれる
    Console.WriteLine(x.ToString());
}

ジェネリックを使う

なるべくジェネリックを使いましょう。

public int CompareTo(IComparable x, int value)
{
    // NG: valueがボックス化される
    return x.CompareTo(value);
}

public int CompareTo(IComparable<int> x, int value)
{
    // OK: valueはintのまま渡される
    return x.CompareTo(value);
}

文字列補間には ToString してから渡す

文字列補間で変数を使用するときはボックス化が発生しないような書き方をしましょう。

※ C# 10 / .NET 6 以降の環境ではこの必要がなくなりました。

int number = 0;

// C# 10 / .NET 6 以前の環境では以下のように書く
// ToString() がないとボックス化が発生する
Console.WriteLine($"数値: {number.ToString()}");

// C# 10 / .NET 6 以後の環境ではこれで良い
Console.WriteLine($"数値: {number}");

参考

Discussion