シンプルで正しい答えは、「C# 言語仕様にそう書いてあるから」です。
明らかに、あなたはその答えに満足しておらず、「なぜそう言っているのか」を知りたがっています。「信頼できるおよび/または公式の情報源」を探していますが、それは少し難しいでしょう. これらの設計上の決定はずっと前になされたもので、13 年というのはソフトウェア エンジニアリングの長い人生です。Eric Lippert が呼んでいるように、それらは「古いタイマー」によって作成されました。彼らはより大きくより良いものに移行しており、公式の情報源を提供するためにここに回答を投稿していません.
ただし、単に信頼できるというリスクがありますが、推測することはできます。C# などのマネージ コンパイラには、.NET 仮想マシン用のコードを生成する必要があるという制約があります。そのルールは、CLI 仕様で慎重に (そして非常に読みやすく) 説明されています。これは Ecma-335 仕様で、ここから無料でダウンロードできます。
パーティション III の 3.1 章と 3.2 章に目を向けてください。add
これらは、加算を実行するために使用できる 2 つの IL 命令について説明していますadd.ovf
。表 2「2 進数演算」へのリンクをクリックすると、これらの IL 命令で使用できるオペランドが説明されています。そこにリストされているタイプはほんのわずかであることに注意してください。byte と short だけでなく、すべての unsigned 型が欠落しています。int、long、IntPtr、および浮動小数点 (float と double) のみが許可されます。x でマークされた追加の制約を使用すると、int を long に追加することはできません。これらの制約は完全に人為的なものではなく、利用可能なハードウェアで合理的に効率的に実行できることに基づいています。
マネージ コンパイラは、有効な IL を生成するためにこれに対処する必要があります。それは難しいことではありません。ushort をテーブルにあるより大きな値の型に変換するだけです。この変換は常に有効です。C# コンパイラは、表に示されている次に大きな型である int を選択します。または一般に、いずれかのオペランドを次に大きい値の型に変換して、両方が同じ型になり、表の制約を満たすようにします。
しかし、ここで新たな問題が発生しました。C# プログラマーをかなり悩ませる問題です。加算の結果は昇格型です。あなたの場合、それはintになります。したがって、たとえば 0x9000 と 0x9000 の 2 つの ushort 値を追加すると、完全に有効な int の結果 0x12000 が得られます。問題は、それが ushort に収まらない値であることです。値がオーバーフローしました。しかし、IL の計算ではオーバーフローしませんでした。オーバーフローするのは、コンパイラが ushort に詰め込もうとしたときだけです。0x12000 は 0x2000 に切り捨てられます。10ではなく、2本または16本の指で数えたときにのみ意味をなす、当惑するような異なる値.
add.ovf 命令がこの問題に対処していないことは注目に値します。オーバーフロー例外を自動発生させる命令です。しかし、そうではありません。変換された int の実際の計算はオーバーフローしませんでした。
ここで、実際の設計上の決定が行われます。昔の人たちは、単に int の結果を ushort に切り捨てることはバグ工場だと判断したようです。確かにそうです。彼らは、加算がオーバーフローする可能性があり、それが発生しても大丈夫であることを知っていることを認めなければならないと判断しました. 彼らはそれをあなたの問題にしました。主な理由は、それを自分たちのものにし、それでも効率的なコードを生成する方法を知らなかったからです。キャストする必要があります。はい、それは腹立たしいことです。あなたもその問題を望んでいないと確信しています。
注目に値するのは、VB.NET の設計者がこの問題に対して別の解決策をとったことです。彼らは実際にそれを問題にし、責任を負いませんでした。2 つの UShort を追加して、キャストなしで UShort に割り当てることができます。違いは、VB.NET コンパイラが実際に追加の IL を生成してオーバーフロー状態をチェックすることです。これは安価なコードではなく、すべての短い追加が約 3 倍遅くなります。しかし、それ以外の点では、Microsoft が非常に類似した機能を持つ2 つの言語を維持している理由を説明しています。
簡単に言うと、最新の cpu アーキテクチャとあまりよく一致しないタイプを使用しているため、代償を払っています。それ自体が、ushort の代わりに uint を使用する本当に良い理由です。ushort から牽引力を引き出すのは困難です。それらを操作するコストがメモリの節約を上回る前に、それらの多くが必要になります。CLI 仕様が制限されているだけでなく、x86 コアは、マシン コードのオペランド プレフィックス バイトが原因で、16 ビット値をロードするために余分な CPU サイクルを必要とします。それが今日でも当てはまるかどうかは実際にはわかりませんが、サイクルのカウントにまだ注意を払っていたときに戻ってきました. 一年前の犬。
VB.NET コンパイラが生成するのと同じコードを C# コンパイラに生成させることで、これらの見苦しくて危険なキャストについては安心できることに注意してください。したがって、キャストが賢明でないことが判明した場合、OverflowException が発生します。[プロジェクト] > [プロパティ] > [ビルド] タブ > [詳細設定] ボタン > [算術オーバーフロー/アンダーフローをチェック] チェックボックスをオンにします。デバッグ ビルド専用です。このチェックボックスがプロジェクト テンプレートによって自動的にオンにならない理由は、もう 1 つの非常に不可解な質問です。決定はかなり前に行われました。