2/10のブログの補足。

csharplangリポジトリ内にいくつか提案ドキュメントが上がり始めたというものの中で、2点ほど取り上げて紹介。この2個だけ、ちょっと以前からの進展があったものです。

読み取り専用参照

最近、C#でも構造体を使ったパフォーマンス改善をいろいろやろうとしているわけですが。 参照戻り値とかはそのための機能ですし、 タプルは内部的にmutable(書き換え可能)な構造体になっています(パフォーマンス的にはそれが一番いい)。

ただ、大き目の構造体の受け渡しは、値渡し(コピーが発生)の負担が大きいです。 なので、例えば以下のように、参照引数を使ったりします。

static void AddTo(ref Matrix4x4 x, ref Matrix4x4 y)
{
    x.M11 += x.M12;
    // 後略
    // 4×4行列なので15行ほど同じようなの
}

ここで問題は、このコード、xyのどちらが書き換わるのかわからないところ。 xの方は書き換える前提で参照渡しをしていますが、 yの方は書き換えるつもりがない。 でも、値渡しするとコピー負荷が高いんで、やむなく参照渡しにしている。 という状態。

こういうのは、意図を明示できるべきだし、もし意図に反してyを書き換えようとしたらコンパイル エラーになるべきです。 そこで、「読み取り専用参照」が提案されていました。 C++ならよくやるやつです。const T&。 C#に対するこれまでの提案ではreadonly refなんかが上がっていたんですが、 今回inキーワードを使うのはどうだろうという話になりました。

static void AddTo(ref Matrix4x4 x, in Matrix4x4 y)
{
    x.M11 += x.M12;
    // 後略
}

前々からreadonly refだと長すぎて嫌だしという話はでていまして。 要するに、

  • 参照渡しの特殊形としてout引数があるんなら、その逆のin引数があってもいいじゃない
  • inなら、foreachやジェネリックの反変性で使ってて今もキーワードだし(破壊的変更になりにくい)

ということで、in参照引数にしてはどうかということになっているみたいです。

IntPtrに対する演算子

去年の年末に「小ネタ」で書きましたが、System.IntPtrSystem.UIntPtrはプリミティブ型です。 要するに、専用のILを持っていて、高速な計算ができる型です。 というか、IntPtrUIntPtrは、ILの内部的にはnative int, native unsigned intという名前の型になっています。 CPUのバスサイズ(32ビットCPUであれば32ビット、64ビットCPUであれば64ビット)の、実行環境依存の整数型。

最近だと、C#もいろんな場所で動かすようになってきました。 Xamarinや.NET Coreのおかげでクロスプラットフォームになっています。 その結果、このnative intをC#上でも使う機会が増えています。 (まあ、クロスプラットフォームを意識したnative相互運用をしたりといった限られた用途ですけども。 以前よりは必要性が高まっているのは確かです。)

ところが、C#上では、IntPtrUIntPtrに対する演算子が一切使えない。 IL的には持っている専用命令を一切活用できない。 #ifでプラットフォームごとにintもしくはlongにしてから計算とかが必要でした。 ということで、IntPtrUIntPtrに対して、一通りの演算子を使えるようにしたという話が出ているようです。