web-dev-qa-db-ja.com

C#でunsigned intを使用しないでください。

最近、C#での符号なし整数の使用について考えました(他の「高水準言語」についても同様の議論が言えると思います)。

整数が必要な場合、通常は整数のサイズのジレンマに直面していません。例として、Personクラスのageプロパティがあります(ただし、質問はプロパティに限定されません)。そのことを念頭に置いて、私が見る限り、符号付き整数( "int")よりも符号なし整数( "uint")を使用する利点は、読みやすさだけです。年齢がポジティブであるという考えを表現したい場合、年齢タイプをuintに設定することでこれを実現できます。

一方、符号なし整数の計算では、あらゆる種類のエラーが発生する可能性があり、2つの年齢の減算などの演算を実行することが困難になります。 (これが理由の1つであると読みましたJava符号なし整数を省略しました)

C#の場合、セッターのガード句は2つの世界のベストを与えるソリューションであると考えることもできますが、たとえば、年齢が何らかのメソッドに渡される場合、これは当てはまりません。回避策は、Ageと呼ばれるクラスを定義し、プロパティageだけをそこに置くことですが、このパターンでは、Meが多くのクラスを作成し、混乱の原因になります(他の開発者は、オブジェクトが単なるラッパーである場合を知りません。そしてそれがより洗練されたものであるとき)。

この問題に関する一般的なベストプラクティスは何ですか?このタイプのシナリオにはどのように対処すればよいですか?

24
Belgi

.NET Frameworkの設計者は、いくつかの理由により、「汎用番号」として32ビットの符号付き整数を選択しました。

  1. 負の数、特に-1を処理できます(フレームワークがエラー条件を示すために使用します。これが、負の数がインデックス作成コンテキストで意味を持たない場合でも、signed intがインデックス作成が必要なすべての場所で使用される理由です)。
  2. それはほとんどの目的を果たすのに十分な大きさですが、ほとんどどこでも経済的に使用するのに十分小さいです。

Unsigned intを使用する理由はnot読みやすさです。 unsigned intだけが提供する数学を取得する機能があります。

ガード句、検証、および契約の前提条件は、有効な数値範囲を保証するための完全に許容できる方法です。実際の数値範囲が0と2の間の数値に正確に対応することはめったにありません32-1(またはネイティブの数値範囲が選択した数値タイプのもの)なので、uintを使用してインターフェイスコントラクトを正の数に制限することは、一種の要点ではありません。

24
Robert Harvey

一般に、可能な限り最も具体的なデータ型を常に使用する必要があります。

たとえば、Entity Frameworkを使用してデータベースからデータをプルしている場合、EFはデータベースで使用されているデータ型に最も近いデータ型を自動的に使用します。

これにはC#で2つの問題があります。
最初に、ほとんどのC#開発者はintのみを使用して整数を表します(longを使用する理由がない限り)。つまり、他の開発者はデータ型をチェックすることを考えないため、上記のオーバーフローエラーが発生します。 2番目のより重要な問題は、.NETの 元の算術演算子intuintlongulongfloat、double、およびdecimal *のみをサポートすることです。これは今日でも同じです( C#5.0言語仕様 のセクション7.8.4を参照)。これは、次のコードを使用して自分でテストできます。

byte a, b;
a = 1;
b = 2;
var c = a - b;      //In visual studio, hover over "var" and the tip will indicate the data type, or you can get the value from cName below.
string cName = c.GetType().Namespace + '.' + c.GetType().Name;

byte-byteの結果はintSystem.Int32)です。

これらの2つの問題により、「整数のみintを整数に使用する」という慣習が生じました。

したがって、あなたの質問に答えるために、C#では通常、intを使用することをお勧めします。

  • 自動化されたコードジェネレーターが別の値(Entity Frameworkなど)を使用しました。
  • プロジェクトの他のすべての開発者は、あまり一般的でないデータ型を使用していることを認識しています(データ型を使用したこととその理由を指摘するコメントを含めます)。
  • あまり一般的でないデータタイプは、プロジェクトですでに一般的に使用されています。
  • プログラムには、あまり一般的でないデータ型の利点が必要です(これらの1億はRAMに保持する必要があるため、byteintまたはintlongの違いが重要です。言及されています)。

データの計算を行う必要がある場合は、一般的なタイプを使用してください。
覚えておいてください、あるタイプから別のタイプにキャストできます。これはCPUの観点からは効率が低下する可能性があるため、7つの一般的なタイプのいずれかを使用するほうがよいでしょうが、必要に応じて選択できます。

列挙( enum )は、上記のガイドラインに対する個人的な例外の1つです。オプションが少ししかない場合は enumを指定 をバイトまたはショートにします。フラグ付き列挙型の最後のビットが必要な場合は、タイプをuintに指定して、16進数を使用してフラグの値を設定できるようにします。

値を制限するコードでプロパティを使用する場合は、概要タグで、どのような制限があり、その理由を説明してください。

*これはC#の質問なので、System.Int32などの.NET名の代わりにC#エイリアスが使用されます。

注:.NET開発者からのブログまたは記事(私にはわかりません)があり、算術関数の数が限られていること、およびそれらが心配しなかったいくつかの理由を指摘していました。私が覚えているように、彼らは他のデータ型のサポートを追加する計画はないことを示しました。

注:Javaは符号なしデータ型をサポートせず、以前は8ビットまたは16ビットの整数をサポートしていませんでした。多くのC#開発者がJavaバックグラウンドまたは両方の言語で作業する必要がある場合、一方の言語の制限が他方の言語に人為的に課されることがあります。

8
Trisped

主に、2つのことに注意する必要があります。それは、表すデータと、計算の中間ステップです。

通常、負の年齢は考慮しないため、年齢をunsigned intにするのは当然のことです。しかし、あなたはある年齢から別の年齢を引くことについて言及します。ある整数を別の整数から盲目的に減算すると、以前に負の年齢が意味をなさないと同意した場合でも、負の数になる可能性があります。したがって、この場合は、計算を符号付き整数で実行する必要があります。

符号なしの値が悪いかどうかについては、符号なしの値が悪いと言うのは非常に一般化していると思います。 Javaは、あなたが言及したように、符号なしの値を持たず、常に私を困らせます。byteは0-255または0x00-0xFFの値を持つことができます。ただし、 127(0x7F)より大きいバイトをインスタンス化するには、それを負の数として書き込むか、整数をバイトにキャストする必要があります。最終的には次のようなコードになります。

byte a = 0x80; // Won't compile!
byte b = (byte) 0x80;
byte c = -128; // Equal to b

上記は、私を終わりまで悩ませます。バイトを扱うほとんどの正気な人にとっては完全に有効な値ですが、バイトの値を197にすることは許可されていません。整数をキャストしたり、負の値(この場合は197 == -59)を見つけたりできます。これも考慮してください:

byte a = 70;
byte b = 80;
byte c = a + b; // c == -106

ご覧のとおり、有効な値を持つ2バイトを追加し、有効な値を持つバイトで終わると、符号が変わってしまいます。それだけでなく、70 + 80 == -106であることもすぐにはわかりません。技術的にはこれはオーバーフローですが、私の考えでは(人間として)、バイトが0xFF未満の値でオーバーフローすることはありません。紙の上でビット演算を行うとき、8番目のビットを符号ビットとは見なしません。

私はビットレベルで多くの整数を扱いますが、すべてに符号を付けると、通常、すべてが直感的でなく、扱いにくくなります。負の数を右シフトすると、新しい1sが数。一方、符号なし整数を右シフトすると、それは行われません。例えば:

signed byte b = 0b10000000;
b = b >> 1; // b == 0b1100 0000
b = b & 0x7F;// b == 0b0100 0000

unsigned byte b = 0b10000000;
b = b >> 1; // b == 0b0100 0000;

それは私が必要ではないと思う余分なステップを追加するだけです。

上記でbyteを使用しましたが、32ビットおよび64ビット整数にも同じことが当てはまります。 unsignedがないことは不自由であり、Javaのような高レベルの言語がまったく許可されていないことを私に衝撃を与えます。しかし、ほとんどの人にとって、これは問題ではありません。なぜなら、多くのプログラマはビットレベルの算術を扱っていないからです。

結局のところ、ビットと見なす場合は符号なし整数を使用すると便利です。数値と見なす場合は符号付き整数を使用すると便利です。

7
Shaz