C#調査ノート

# はじめに
この文章はコーディングガイドライン作成のために調査・実験したノートです。
ごく短い時間で応答を返さなければならないソフトウェアを作る仕事のため、
と思っていただければありがたいです。
あと、最低でも24時間連続稼働できる必要があります。場合によっては、数年稼働状態もあり得ます。
更新するかもしれないし、しないかもしれません。

# コード
コードはチームの共有物です。
あなたが書いたコードの責任をあなただけに押し付けることはありません。
チームメイトの書いたコードはあなたにも責任があります。

# struct
classと上手く使い分けてください。十数バイト程度なら効率が良くなる場合もありますし、
メモリ効率という点でも有用です。nullを許容しない点も重要です。

# 拡張メソッド
[拡張メソッド使用に関する一般的ガイドライン](https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/classes-and-structs/extension-methods#general-guidelines)
とりあえずこれを読んで、それでも必要と判断したなら使ってもいいですよ、という感じです。
Enumを拡張するぐらいは許してよいと思いますが、それもやりすぎ注意という感じでしょうか。
だったら素直にstructでも作ればいいと思いますよ。
チーム外のクラスライブラリを拡張することはやめましょう。

# partial
通信データ定義が良い例ですが、自動生成したコードの拡張に有用です。
通信データ定義はC#のpartialを付加できるようにしています。

# ラムダ式
少なくとも中間コードレベルでは、全く同じ式でも別の定義が生成されます。
ただの静的関数の場合とクラスメソッドが生成される場合があります。

ローカル変数をキャプチャするために、コンパイラがクラスを生成します。
参照型のクラスを利用して、ローカル変数を保持しておくということです。
Javaの無名クラスをイメージしてください。
コンパイラにより生成されたクラスを経由してローカル変数の参照が保持されます。

クラスメソッドの場合、そのクラスへの参照が保持されることに注意しましょう。
delegateがメモリリークを引き起こすわけではありません。
プログラマが正しく使用していないから、不具合を引き起こすのです。

# LINQ
LINQが絶対に実現できないことがあることを理解しましょう。
Database Management Systemがなぜ高速に検索できるのか？を理解しましょう。

```cs
class Program
{
    static System.Random random = new System.Random(123456789);
    static System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();

    private static void swap<T>(ref T x0, ref T x1)
    {
        T tmp = x0;
        x0 = x1;
        x1 = tmp;
    }

    private static void measure(out double duration0, out double duration1, int numSamples)
    {
        int[] order = new int[numSamples];
        int[] origin = new int[numSamples];
        int[] data0 = new int[numSamples];
        int[] data1 = new int[numSamples];
        bool[] results0 = new bool[numSamples];
        bool[] results1 = new bool[numSamples];
        for(int i = 0; i<numSamples; ++i) {
            int v;
            while(0 == (v = random.Next()));
            origin[i] = data0[i] = data1[i] = v;
            order[i] = i;
        }

        for(int i = 0; i<numSamples; ++i) {
            int j = random.Next() % numSamples;
            swap<int>(ref order[i], ref order[j]);
        }

        {
            stopwatch.Restart();
            System.Array.Sort<int>(data0);
            for(int i = 0; i<numSamples; ++i) {
                int x = origin[order[i]];
                results0[i] = 0<=System.Array.BinarySearch<int>(data0, x);
            }
            stopwatch.Stop();
            duration0 = stopwatch.Elapsed.TotalMilliseconds / numSamples;
        }
        {
            stopwatch.Restart();
            for(int i = 0; i<numSamples; ++i) {
                int x = origin[order[i]];
                results1[i] = x == data1.FirstOrDefault(n => n==x);
            }
            stopwatch.Stop();
            duration1 = stopwatch.Elapsed.TotalMilliseconds / numSamples;
        }
        for(int i=0; i<numSamples; ++i) {
            if(results0[i] != results1[i]) {
                System.Console.WriteLine("{0}: {1} != {2}", i, results0[i], results1[i]);
            }
        }
    }

    static void Main(string[] args)
    {
        {
            int[] NumSamples = new int[] { 128, 256, 512, 1024, 2048, 4096 };
            double duration0, duration1;

            System.Console.WriteLine(",sort,linq,");
            for(int i=0; i<NumSamples.Length; ++i) {
                measure(out duration0, out duration1, NumSamples[i]);
                System.Console.WriteLine("{0}, {1}, {2}", NumSamples[i], duration0, duration1);
            }
        }
    }
}
```

# まとめ