[C#] 静的メンバと静的クラス

##静的メンバ
- 特定のインスタンスにではなく，クラスに属するフィールドやメソッド，プロパティのこと．
- `static`修飾子をつけると静的メンバを宣言できる．
- 静的メンバはアプリケーションにただ１つだけ生成され，すべてのインスタンスの間で共有される．
- 静的メンバへのアクセスには，クラスインスタンスを生成するのではなく，クラス名を直接指定する．
- 静的フィールドの初期化には，静的コンストラクターを利用する．
- 通常のコンストラクターがインスタンス生成時に毎回呼び出されるのに対して， 静的コンストラクターはそのクラスの何れかのメンバに初めてアクセスしたときに1度だけ呼び出される．
- 静的メンバから，通常のインスタンス変数やインスタンスメソッドへはアクセスできない．

###実装例：静的メンバの作成

- 「果物」を表す`Fruit`クラスに対し，番号プロパティ`int Id`, 名前プロパティ`string Name`，コンストラクタ，文字列変換メソッド`string ToString()`を与える．
    - これらは，インスタンスに依存する通常のローカルメンバであり，インスタンス毎に異なる値を持つ．
- 番号から果物への単射影を管理するための`private`静的フィールド`Dictionary<int, Fruit> _fruits`を与える．
- 静的コンストラクタを用意し，`_fruits`へ初期値を与える．
- 番号指定で`Fruit`クラスインスタンスを作成するための`FromId(int id)`静的メソッドを与える．
    - これらの静的メンバは，インスタンスに依存せずクラス間で共有される．

```c#
/// <summary>
/// 果物を表します．
/// </summary>
public class Fruit
{
    /// <summary>
    /// 番号
    /// </summary>
    public int Id { get; private set; }

    /// <summary>
    /// 名前
    /// </summary>
    public string Name { get; private set; }

    /// <summary>
    /// 果物図鑑　※静的メンバ（＝クラス間で共通）
    /// </summary>
    private static Dictionary<int, Fruit> _fruitsLibrary { get; set; }

    /// <summary>
    /// コンストラクタ ※静的メンバにはアクセス不可
    /// </summary>
    /// <param name="id">番号</param>
    /// <param name="name">名前</param>
    public Fruit(int id, string name) => (this.Id, this.Name) = (id, name);

    /// <summary>
    /// 静的コンストラクタ ※静的メンバのみにアクセス可能
    /// </summary>
    static Fruit()
    {
        // 果物図鑑を作成
        _fruitsLibrary = new Fruit[] { new Fruit(1, "りんご"), 
                                       new Fruit(2, "いちご"), 
                                       new Fruit(3, "メロン") }
                                      .ToDictionary(f => f.Id, f => f);
    }
        
    /// <summary>
    /// 現在のオブジェクトを表す文字列を返します．
    /// </summary>
    public override string ToString() => $"{this.Id:00}：{this.Name}";

    /// <summary>
    /// 番号を指定して果物図鑑からインスタンスを取得します．
    /// </summary>
    /// <param name="id">番号</param>
    public static Fruit FromId(int id) => _fruits[id];
}
```

###実装例：メンバへのアクセス方法

- 通常のローカルメンバは，クラスインスタンスから呼び出しを行う．
- 静的メンバは，クラスをインスタンス化せず，[クラス名.メンバ]の記載形式で呼び出しを行う．

```c#
// 通常のプロパティやメソッドは，クラスをインスタンス化してからアクセス
var fruit1 = new Fruit(4, "パイナップル");
var id = fruit1.Id;                 // int[4]    
var display = fruit1.ToString();    // string["04：パイナップル"]

// 静的メンバは，クラスをインスタンス化せず，[クラス名.メンバ]の記載形式でアクセス
var fruit2 = Fruit.FromId(2);       // Fruit[Id = 2, Name = "いちご"]
var fruit3 = Fruit.FromId(3);       // Fruit[Id = 3, Name = "メロン"]
```

##静的クラス
- 静的メンバしか定義できないクラス．
- クラスの前に`static`修飾子を付けて宣言する．
- インスタンスの作成が不可能なクラスを作る場合に利用する．
- 別言語では *module* と呼ばれ区別されることもある．

##staticの活用
- 例えば，`readonly`でない`public`な静的フィールドはどこからでも書き換えられる可能性があり，むやみな使用は控えるべきである．
- 以下に，`static`修飾子の活用方法を幾つか示す．

###ユーティリティ(ヘルパー)クラスとしての活用
- 複数の箇所から利用される汎用的な処理等，共通の機能を提供するために静的クラスを利用する．

###グローバル定数としての活用
- 定数値のシンボル名が必要で，その値の型を`const`宣言で使用できない場合，またはその値をコンパイル時に計算できない場合は`static readonly`フィールドを利用する．
- `const`は，属性に指定するパラメータや列挙型の定義など，コンパイル時に値が必要な場合にのみ使用する．

|フィールド|値の決定|実行速度|switch文|値の割当|
|:-------:|:-----:|:------:|:------:|:------:|
|*const*|コンパイル時|readonlyより速い|利用可|インスタンスを new した結果は割当不可|
|*static readonly*|実行時|constより**僅かに**遅い|利用不可|インスタンスを new した結果を割当可能／コンストラクタで初期化可能|

##拡張メソッド
- 通常の前置き記法である静的メソッドを，インスタンスメソッドと同様に後置き記法で書くことができる．
- 拡張メソッドの作成には，静的クラスに対して静的メソッドを実装し，先頭の引数に対して`this`修飾子を付与する．

###実装例

```c#
public static class Extensions
{
    /// <summary>
    /// IEnumerableインターフェース型の構築された string コレクションのメンバーを連結します．各メンバーの間には，指定した区切り記号が挿入されます．
    /// </summary>
    /// <param name="values">連結する文字列を格納しているコレクション．</param>
    /// <param name="separater">区切り文字として使用する文字列．戻される文字列に separator が含まれるのは，values に複数の要素がある場合のみです．</param>
    /// <returns>values のメンバーからなる，separator 文字列で区切られた文字列． values にメンバーがない場合，メソッドは tring.Emptyを返します．</returns>
    public static string Join(this IEnumerable<string> values, string separater)
    {
        return string.Join(separater, values);
    }
}
```

###使用例

```c#
var pieces = new List<string>() { "飛車", "角", "金", "銀", "桂馬", "香車" };

// 通常の静的メソッド呼び出し(前置き記法)
var display1 = string.Join(", ", pieces);   // string["飛車, 角, 金, 銀, 桂馬, 香車"]

// 拡張メソッド呼び出し(後置き記法)
var display2 = pieces.Join(", ");           // string["飛車, 角, 金, 銀, 桂馬, 香車"]
```
 
##参考
- [静的メンバー - C# によるプログラミング入門 | ++C++; // 未確認飛行 C](https://ufcpp.net/study/csharp/oo_static.html)
- [【C#の定数】const と readonly，static readonly の使い分け](https://qiita.com/tanakaworld/items/eda69da263e24adfa07d)