BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ ニュース C#の新機能 - 読み取り専用の参照と構造体

C#の新機能 - 読み取り専用の参照と構造体

原文(投稿日:2017/04/19)へのリンク

C++には“const”という機能がある。これはパラメータに対して適用可能で、関数がパラメータやパラメータが参照するオブジェクトを変更しないということを、呼び出し側が認識できる(実際にはもっと複雑だ。詳細はConst Correctnessを参照のこと)。今回の提案により、C#でも同じような機能を利用可能になる。

読み取り専用参照パラメータ

“inパラメータ”としても知られる読み取り専用の参照を使えば、C#でも同様な制限が可能になる。その基本的な考え方は、パラメータを“readonly ref”あるいは“in”とマークすることにより、コンパイラが“このパラメータは参照渡しを行なうことでパフォーマンスを向上させるが、実際には変更しない”ということを理解する、というものだ。この機能は、パフォーマンスの重要なシナリオにおいて、特に大規模な構造体に対して使用される。提案では、次のような例があげられている。

XNAのようなグラフィックライブラリのベクトル/行列演算では、純粋に性能上の理由からrefオペランドが使用されています。Roslynコンパイラ自体にも、メモリ割り当てを避けるために構造体を使用し、コピーのコストを回避するために参照渡しをしているコードがあります。

この構文は、C++の2つのconst機能を組み合わせたものだ。すなわち、パラメータ自体も、パラメータが参照するオブジェクトや構造体のデータも変更できない。

パラメータを参照渡しする場合、現在は“ref”あるいは“out”キーワードを使用する必要がある。今回の提案では、その要件が“in”パラメータには適用されなくなると同時に、式の結果を渡すことも可能になる(これはVBでは現在でも可能だが、C#では許可されていない)。

実装の詳細

オーバーロードのルールは、refとout両方のパラメータに対して、現行と同じように機能する。

議論の余地はあるが、現在の計画では、匿名関数または非同期関数(すなわちクロージャオブジェクトの作成)では、“in”パラメータを取ることはできない。“in”パラメータをキャプチャする場合の問題として、それによってコピーが生成されるため、コピーのパフォーマンスコスト回避という“in”を使用する目的が無効になる、というのがその理由だ。

パラメータを“readonly ref”あるいは“in”とマークしても、参照する値が不変(immutable)になる訳ではない。パラメータを宣言した関数で変更することはできなくても、それ以外の場所では変更が可能だ。マルチスレッドが必要なのではなく、パラメータが参照する元の変数にアクセスする方法を指定しているに過ぎない。

構造体のメソッド呼び出しにも問題がある。提案によると、

構造体の通常のインスタンスメソッドでは、インスタンスを変更したり、あるいはthisを参照公開(ref-expose)する可能性があるため、レシーバが読み取り専用フィールドである場合と同じように、中間コピーを生成する必要があります。

ただし、下位互換性に関する考慮がなく、回避策が存在することから、コンパイラは警告を表示して、暗黙的のコピーが実行されることを明確にする必要があります。

"out"パラメータと同様に、“in”パラメータが必要であることを示す特別なアトリビュートが示されるようになる。旧版のコンパイラはこのパラメータを無視するので、下位互換性の問題はない。

読み取り専用参照戻り値

この機能に密接に関連するのが、参照戻り値を読み取り専用としてマークする機能だ。“in”パラメータと同じく、この機能の主な目的はパフォーマンスにある。ただしこの場合には、式の結果を返すことはできず、配列要素やRefパラメータ、オブジェクトのフィールドなど、通常のRef戻り値として有効な変数でなければならない。

ref/in 拡張メソッド

“ref”拡張メソッドは、前述の拡張メソッドにおいて、渡された構造体の変更を可能にする。コンパイラでは、ref拡張メソッドに渡される引数が変更可能であることを保証する必要がある。

“in”拡張メソッドは変更を許可しないが、それでもパフォーマンスが重要なコードでは、特に構造体が大きな場合には有効だ。この場合はもちろん、引数が変更可能である必要はない。

いずれの場合においても、この機能は構造体でのみ使用可能である。

編集者記: もしPure属性が広範に使用されていたならば、“in”拡張メソッドによる非純粋メソッドの呼び出しを禁止することも可能であったはずだ。しかし、これではパフォーマンスが改善されないため、そのようなことにはならないだろう。

読み取り専用構造体

構造体変数を読み取り専用とマークすることは、パフォーマンスに影響する。 コンパイラは通常、メソッド呼び出しが構造体を変更可能であるかどうかを知らないため、変更されるという前提の下で、読み取り専用の構造体変数にもコピーを生成する。

この機能を使用すれば、構造体全体を型レベルで読み取り専用としてマークすることができる。この指定を行なうことで、読み取り専用の構造体が読み取り専用の構造体変数で公開される場合、コピーが不要であることをコンパイラに伝えることが可能になる。

提案には次のように記されている。

唯一の明白な疑問は、一部のメソッドを例外としてオプトアウトするオプションが必要かどうかです。

今までのところ、メンバ単位での読み取り専用属性のコントロールは必要ではありませんし、必要になれば後で追加することも可能です。

現時点では、可変/不変を“混合”した構造体は一般的ではない、ということを前提にしています。もし部分的に可変であったとしても、構造体変数には一般的に左辺値であることが必要なので、暗黙的なコピーの影響を受けることはありません。

欠点

提案によると、これらの機能は既存のコードには有効ではないが、以下のような新たなシナリオではメリットがある。

  • 計算時間で課金され、応答性が競争上のメリットとなるクラウドおよびデータセンタでのシナリオ。
  • レイテンシにおいて準リアルタイム性を必要とするゲームやVR、AR。

呼び出される関数が“in”パラメータの制限を迂回できるという点も警告されているが、“out”パラメータで既に同じことが可能であるため、これは些細な問題に過ぎない。

 
 

この記事を評価

関連性
スタイル
 
 

この記事に星をつける

おすすめ度
スタイル

BT