きっかけ
最近読んだコードが クソ 伸び代だらけで、 ムシャクシャていた 仏の気持ちになったので書きました。
C言語の知識しかない状態で C# を書くと大勢の人が似たようなコードに行き着いてしまうと思います。
そこで、どう書くと C# らしくなるのかを自分なりにまとめていきます。
関連記事一覧
第一弾(本記事)
第二弾
直したいポイント
命名規則
命名規則はプロジェクトで定められたものに従うのが勿論ですが、言語としての推奨があるので意識してほしいですよね。
名前付けのガイドライン - Microsoft Docs
Qiita にも記事がありました。
C# CODING GUIDELINES @Ted-HM
特に気になってしまうのは以下の2点でしょう。
// スネークケースは推奨されていない。
public const int HOGE_HOGE = 1;
// ハンガリアン記法も過去のもの。
public int iHoge { get; set; }
public string strHoge { get; set; }
特に命名規則において .NET のクラスライブラリは最大のお手本ですので、自身が書いたコードと見比べてみるのがいいと思います。
(VisualStudio でクラス等にカーソルをあわせて、F12 を押しましょう!)
配列の呪縛 その1
public void Fuga(string[] args)
{
for (int i = 0; i < args.Length; i++)
{
// args[i] に何か処理。
}
}
このコードは間違いではないですが、以下のデメリットがあります。
- インデックサ
args[i]
の部分は些細なミスで例外を引き起こす。 - 引数が配列型に固定されている。
なので、このように変更します。
public void Hoge(IEnumerable<string> args)
{
foreach (var item in args)
{
// item に何か処理。
}
}
このコードでは、 for -> foreach の変更以外に、引数の型が IEnumerable<string> に変化しました。
単なる foreach で済むメソッドであれば、この様な引数を定義するのがベストです。
そうすると、以下の様に1つのメソッドで様々な型の引数を受け取れる汎用性が獲得できるのです!
// どれもエラーにならない!
Hoge(new string[] { });
Hoge(new List<string>());
Hoge(new Collection<string>());
これにはインターフェイスが関連しているのですが、ここを理解できれば C# のレベルがグッと上がります。
(欲を言えば Linq を扱えるまでレベルアップしてほしい。)
配列の呪縛 その2
メソッドの戻り値やプロパティ等でも配列を使いがちです。
そして、こんなコードを書いてしまいます。
public string[] Fuga()
{
var list = new List<string>();
// あれこれ list.Add() する処理
return list.ToArray();
}
前述の呪縛 その1 で引数が配列になっているため、それに合わせる為だというのが理由の一つだと思います。
しかし、それがすでに解決済みならば List<string> を直接 return でき、 ToArray() による無駄なオブジェクトの生成を減らすことができます。
さらに戻り値の型を IReadOnlyList<string> や IEnumerable<string> に変える事で、メソッドの外で Add() 等の変更が不可能になり、意図しないミスを防ぐ事ができます。
public IReadOnlyList<string> Fuga()
{
var list = new List<string>();
// あれこれ list.Add() する処理
return list;
}
var fuga = Fuga();
// 読み出しはできる
foreach (var item in fuga) { }
var tmp = fuga[0];
// 書き込みはできない
// コンパイルエラー
fuga.Add("");
fuga[0] = "";
おまけ
foreach 可能で readonly なインターフェイスを以下にまとめました。
用途に合わせて必要十分なインターフェイスを選定してください。
(もちろんすべて配列と互換があります。)
インターフェイス名 | foreach | サイズ (int Count(){get;}) | インデックサ (T this[int]{get;}) |
---|---|---|---|
IEnumerable<T> | ○ | × | × |
IReadOnlyCollection<T> | ○ | ○ | × |
IReadOnlyList<T> | ○ | ○ | ○ |