C#のuintとは?intとの違い・範囲・型変換・使いどころを初心者向けに解説

はじめに

C#のuintは、0以上の整数だけを扱うためのデータ型です。正式には「符号なし32ビット整数型」と呼ばれ、intと同じ32ビット・4バイトのサイズを持ちながら、負の数を扱わない代わりに、より大きな正の整数を表現できます。

ただし、初心者が「マイナスにならない値なら全部uintにしよう」と考えるのは注意が必要です。C#では、通常のカウンター、ループ変数、配列やListのインデックスなどにはintがよく使われます。uintは、ビット演算、外部API、ファイル形式、C/C++との相互運用など、32ビットの符号なし整数であること自体に意味がある場面で使うのが基本です。

この記事では、C#のuintについて、intとの違い、値の範囲、型変換、計算時の注意点、実際の使いどころまで初心者向けに解説します。

1. C#のuintとは?符号なし32ビット整数型の基本

1-1. uintは「0以上の整数」だけを扱うデータ型

C#のuintは、0以上の整数だけを扱うデータ型です。uintでは、011004294967295のような値を表現できますが、-1-100のような負の値は通常扱えません。

C#
uint count = 100;
uint score = 0;

uintは「unsigned integer」の略で、日本語では「符号なし整数」と呼ばれます。符号なしとは、プラス・マイナスを表す符号を持たないという意味です。そのため、すべてのビットを正の値の表現に使えます。

1-2. uintはSystem.UInt32の別名

C#のuintは、.NETのSystem.UInt32型の別名です。つまり、次の2つの宣言は同じ意味です。

C#
uint a = 100;
System.UInt32 b = 100;

通常のC#コードでは、読みやすさを重視してuintと書くことが多いです。一方、APIドキュメントや.NETの型名としてはSystem.UInt32またはUInt32と表記されることがあります。

MicrosoftのC#リファレンスでも、uintは0〜4,294,967,295の範囲を持つ符号なし32ビット整数で、.NET型としてはSystem.UInt32に対応すると説明されています。

1-3. int・uint・longなど整数型全体の中での位置づけ

C#には、整数を扱うための型が複数あります。主な型を整理すると、次のようになります。

符号サイズ主な用途
byte符号なし8ビットバイナリデータ、0〜255の値
short符号付き16ビット小さい整数
ushort符号なし16ビット0以上の小さい整数
int符号付き32ビット一般的な整数
uint符号なし32ビット0以上の32ビット整数
long符号付き64ビット大きな整数
ulong符号なし64ビット0以上の非常に大きな整数

この中で、通常の整数計算に最もよく使われるのはintです。uintは、負の値を使わないだけでなく、「32ビット符号なし整数として扱う必要がある」場合に使われます。

1-4. uintの読み方と「unsigned int」との関係

uintは「ユーイント」または「ユーインティー」と読まれることが多いです。意味としては「unsigned int」、つまり「符号なしint」です。

CやC++ではunsigned intという型名が使われますが、C#ではそれに近い型としてuintが用意されています。ただし、C#のuintは常に32ビットのSystem.UInt32です。C/C++のunsigned intは環境や仕様によって扱いが異なることがあるため、相互運用する場合はサイズや範囲を確認する必要があります。

2. uintの範囲・サイズ・初期値

2-1. uintで扱える値の範囲は0〜4,294,967,295

uintで扱える値の範囲は、次のとおりです。

0 〜 4,294,967,295

最小値は0、最大値は4,294,967,295です。負の値は範囲外です。

C#
uint min = 0;
uint max = 4_294_967_295U;

C#では桁区切りとして_を使えるため、大きな数値は4_294_967_295Uのように書くと読みやすくなります。

2-2. uintのサイズは32ビット・4バイト

uintのサイズは32ビット、つまり4バイトです。これはintと同じサイズです。

C#
Console.WriteLine(sizeof(uint)); // 4

uintは32個のビットをすべて0以上の整数の表現に使います。そのため、同じ32ビットでも、符号付きのintより大きな正の値を扱えます。

2-3. uintの既定値は0

uintの既定値は0です。C#の整数型の既定値はすべて0であり、uintも例外ではありません。

C#
uint value = default;
Console.WriteLine(value); // 0

クラスのフィールドとしてuintを宣言した場合、明示的に値を代入しなければ0で初期化されます。

C#
class Player
{
public uint Score;
}

var player = new Player();
Console.WriteLine(player.Score); // 0

2-4. uint.MinValueとuint.MaxValueで範囲を確認する方法

uintの最小値と最大値は、uint.MinValueuint.MaxValueで確認できます。

C#
Console.WriteLine(uint.MinValue); // 0
Console.WriteLine(uint.MaxValue); // 4294967295

ハードコーディングで4294967295と書くよりも、uint.MaxValueを使ったほうが意味が伝わりやすく、ミスも減らせます。

C#
uint value = uint.MaxValue;

2-5. 32ビットなのにintより大きい正の値を扱える理由

intuintも32ビットですが、扱える範囲は異なります。

intは符号付き整数なので、32ビットのうち一部を符号、つまりプラスかマイナスかの表現に使います。そのため、範囲は次のようになります。

-2,147,483,648 〜 2,147,483,647

一方、uintは符号なし整数なので、32ビットすべてを0以上の値の表現に使えます。そのため、範囲は次のようになります。

0 〜 4,294,967,295

同じ32ビットでも、負の値を表現しない代わりに、正の方向へ約2倍広い範囲を持てるということです。

3. uintとintの違い

3-1. intは符号付き、uintは符号なし

intuintの最も大きな違いは、符号付きか符号なしかです。

intは符号付き整数型です。正の値、0、負の値を扱えます。

C#
int a = 100;
int b = -100;

uintは符号なし整数型です。0以上の値だけを扱います。

C#
uint a = 100;
uint b = 0;

3-2. intとuintの値の範囲を比較

intuintの範囲を比較すると、次のようになります。

最小値最大値サイズ
int-2,147,483,6482,147,483,6474バイト
uint04,294,967,2954バイト

uintは負の値を扱えない代わりに、int.MaxValueより大きい値を扱えます。

C#
uint big = 4_000_000_000U; // OK
// int bigInt = 4_000_000_000; // エラー

3-3. 負の値を扱えるかどうかの違い

intは負の値を扱えますが、uintは負の値を直接代入できません。

C#
int temperature = -5; // OK

// uint value = -5; // エラー

在庫数、サイズ、ビットパターンなど、理論上マイナスにならない値であっても、必ずuintを使うべきとは限りません。C#の標準APIではintが使われる場面が多く、uintを選ぶと型変換が増えることがあります。

3-4. メモリサイズはどちらも同じ4バイト

intuintは、どちらも32ビット・4バイトです。uintにしてもメモリ使用量が半分になるわけではありません。

C#
Console.WriteLine(sizeof(int));  // 4
Console.WriteLine(sizeof(uint)); // 4

そのため、メモリ節約の目的でintの代わりにuintを使う意味は基本的にありません。

3-5. C#では一般的にintがよく使われる理由

C#では、一般的な整数にはintがよく使われます。理由は、.NETの多くのAPIがintを前提としているからです。

たとえば、配列の長さ、List<T>Count、文字列の長さなどは、多くの場合intで扱われます。

C#
var list = new List<string> { "A", "B", "C" };

int count = list.Count;
for (int i = 0; i < list.Count; i++)
{
Console.WriteLine(list[i]);
}

ここでuintを使うと、intとの比較や変換が必要になり、コードが複雑になりやすいです。

3-6. 「負の数を使わないならuintでよい」とは限らない理由

「年齢、個数、回数はマイナスにならないからuintでよい」と考える人もいます。しかし、C#ではそれだけの理由でuintを選ぶと不便になることがあります。

主な理由は次のとおりです。

理由内容
APIとの相性intを使う標準APIが多い
型変換が増えるintuintの変換にキャストが必要な場面がある
計算が複雑になる0から1を引くとオーバーフローする
エラー値を表現しにくい-1を「見つからない」などの意味に使えない
可読性が下がる場合があるuintを見た人が「ビット演算用かな?」と考えることがある

そのため、通常の業務ロジックではintを使い、uintは必要性が明確な場面で使うのが基本です。

4. uintの宣言・代入・リテラルの書き方

4-1. uint変数を宣言する基本構文

uint変数は、次のように宣言します。

C#
uint 変数名;

値を代入して宣言する場合は、次のように書きます。

C#
uint count = 10;

変数名は、countscoreflagscolorなど、値の意味がわかる名前にすると読みやすくなります。

4-2. uintに値を代入するサンプルコード

uintには、0以上でuintの範囲内の整数を代入できます。

C#
uint score = 100;
uint itemCount = 0;
uint maxValue = 4_294_967_295U;

Console.WriteLine(score);
Console.WriteLine(itemCount);
Console.WriteLine(maxValue);

123のような小さな整数リテラルは通常intとして扱われますが、定数値がuintの範囲内であればuintに代入できます。

C#
uint value = 123; // OK

4-3. uint型のリテラルにはuまたはUを付ける

uint型のリテラルであることを明示したい場合は、数値の末尾にuまたはUを付けます。

C#
uint a = 100U;
uint b = 200u;

一般的には、見やすさのために大文字のUを使うことが多いです。

C#
uint max = 4_294_967_295U;

C#の整数リテラルでは、Uまたはuサフィックスを付けると、その値を表現できる最初の型としてuintまたはulongが選ばれます。

4-4. varでuintとして推論されるケース

varを使うと、右辺の値から型が推論されます。

C#
var a = 100;   // int
var b = 100U; // uint

100だけだと通常はintとして推論されます。uintとして推論させたい場合は、100UのようにUを付けます。

また、16進数リテラルでintに収まらない値を書いた場合、uintとして推論されることがあります。

C#
var value = 0xFFFF_FFFF; // uint
Console.WriteLine(value); // 4294967295

ただし、コードを読む人に意図を明確に伝えるなら、次のように型を明示するほうがわかりやすいです。

C#
uint value = 0xFFFF_FFFF;

4-5. uintに負の値を代入するとエラーになる例

uintに負の値を直接代入することはできません。

C#
// uint value = -1; // エラー

-1uintの範囲外です。uintは0以上の値だけを扱う型なので、負の値を使う可能性がある場合はintlongを使いましょう。

C#
int value = -1; // OK

5. uintの型変換とキャスト

5-1. uintからlong・ulong・float・doubleへの暗黙的変換

uintのすべての値を安全に表現できる型には、暗黙的に変換できます。たとえば、uintからlongulongfloatdoubleなどへの変換は暗黙的に行えます。

C#
uint value = 4_000_000_000U;

long a = value;
ulong b = value;
float c = value;
double d = value;

ただし、floatは表現できる桁数に限りがあるため、大きな整数では厳密な値を保持できないことがあります。整数として正確に扱いたい場合は、longulongを使うほうが安全です。

C#の数値変換では、変換先の型が変換元のすべての値を格納できる場合は暗黙的変換、それ以外の場合は明示的変換になります。

5-2. uintからintへの変換には明示的キャストが必要

uintからintへは、暗黙的に変換できません。理由は、uintの最大値がintの最大値を超えているからです。

C#
uint value = 100;

// int number = value; // エラー
int number = (int)value; // OK

小さい値であれば問題なく変換できます。

C#
uint value = 100;
int number = (int)value;

Console.WriteLine(number); // 100

しかし、int.MaxValueを超えるuintintに変換すると、uncheckedでは予想外の値になることがあります。

C#
uint value = 4_000_000_000U;
int number = (int)value;

Console.WriteLine(number); // 負の値になる可能性がある

安全に変換したい場合は、範囲を確認してからキャストします。

C#
uint value = 4_000_000_000U;

if (value <= int.MaxValue)
{
int number = (int)value;
Console.WriteLine(number);
}
else
{
Console.WriteLine("intの範囲を超えています");
}

5-3. intからuintへの変換で注意すべき負の値

intからuintへの変換にも注意が必要です。intには負の値があるため、そのままuintに変換すると問題が起きることがあります。

C#
int value = -1;

// uint number = value; // エラー
uint number = (uint)value; // 明示的キャストは可能

uncheckedコンテキストでは、-1uintに変換すると4294967295になることがあります。

C#
unchecked
{
int value = -1;
uint number = (uint)value;

Console.WriteLine(number); // 4294967295
}

これは、同じビット列をuintとして解釈し直すためです。初心者のうちは、負の値をuintにキャストするコードは避けたほうが安全です。

5-4. checkedとuncheckedによるオーバーフローの違い

C#には、オーバーフローを検出するcheckedと、オーバーフローを許容するuncheckedがあります。

C#
uint value = uint.MaxValue;

unchecked
{
uint result = value + 1U;
Console.WriteLine(result); // 0
}

uncheckedでは、uint.MaxValue + 1が0に戻ります。これは桁あふれが起きて、値が循環するためです。

一方、checkedではオーバーフローが例外として検出されます。

C#
uint value = uint.MaxValue;

checked
{
uint result = value + 1U; // OverflowException
Console.WriteLine(result);
}

安全性を重視する計算では、checkedを使うと不正なオーバーフローに気づきやすくなります。

5-5. 文字列からuintへ変換するParse・TryParse

文字列をuintに変換するには、uint.Parseまたはuint.TryParseを使います。

C#
string text = "123";

uint value = uint.Parse(text);
Console.WriteLine(value); // 123

ただし、Parseは変換できない文字列を渡すと例外を投げます。

C#
string text = "abc";

// uint value = uint.Parse(text); // FormatException

ユーザー入力のように失敗する可能性がある場合は、TryParseを使うのが安全です。

C#
string text = "123";

if (uint.TryParse(text, out uint value))
{
Console.WriteLine($"変換成功: {value}");
}
else
{
Console.WriteLine("uintに変換できません");
}

UInt32.Parseは文字列を32ビット符号なし整数に変換するメソッドで、範囲外の値ではOverflowExceptionが発生する可能性があります。UInt32.TryParseは変換の成否をtrueまたはfalseで返します。

5-6. Convert.ToUInt32を使った変換方法

Convert.ToUInt32を使って、文字列や数値をuintに変換することもできます。

C#
uint a = Convert.ToUInt32("123");
uint b = Convert.ToUInt32(456);

Console.WriteLine(a); // 123
Console.WriteLine(b); // 456

ただし、負の値や範囲外の値を変換しようとすると例外が発生します。

C#
int value = -1;

// uint result = Convert.ToUInt32(value); // OverflowException

Convert.ToUInt32は便利ですが、ユーザー入力を扱う場合はTryParseのほうが例外処理を書かずに済むため扱いやすいです。

5-7. 型変換でよく出るコンパイルエラーと対処法

uintでよくある型変換エラーは、intuintを暗黙的に変換しようとしたときに発生します。

C#
uint value = 100;
// int number = value; // エラー

対処法は、範囲を確認してから明示的にキャストすることです。

C#
uint value = 100;

if (value <= int.MaxValue)
{
int number = (int)value;
Console.WriteLine(number);
}

逆に、intからuintへ変換する場合も、負の値でないことを確認しましょう。

C#
int value = 100;

if (value >= 0)
{
uint number = (uint)value;
Console.WriteLine(number);
}

型変換では、「値が範囲内か」「負の値ではないか」「オーバーフローを検出すべきか」を意識することが重要です。

6. uintの計算・比較・オーバーフローの注意点

6-1. uint同士の四則演算の基本

uint同士では、通常の四則演算ができます。

C#
uint a = 10U;
uint b = 3U;

uint sum = a + b;
uint diff = a - b;
uint product = a * b;
uint quotient = a / b;
uint remainder = a % b;

Console.WriteLine(sum); // 13
Console.WriteLine(diff); // 7
Console.WriteLine(product); // 30
Console.WriteLine(quotient); // 3
Console.WriteLine(remainder); // 1

uintは整数型なので、割り算の結果も整数になります。

C#
uint result = 10U / 3U;
Console.WriteLine(result); // 3

小数部分は切り捨てられます。小数を扱いたい場合は、doubledecimalを使いましょう。

6-2. uintとintを混ぜて計算するとエラーになりやすい理由

uintintを混ぜて計算すると、結果の型が直感と違ったり、代入時にエラーになったりすることがあります。

C#
uint a = 10U;
int b = 3;

var result = a + b;
Console.WriteLine(result);

このようなコードでは、計算自体はできても、結果をuintに代入しようとするとエラーになる場合があります。

C#
uint a = 10U;
int b = 3;

// uint result = a + b; // エラーになりやすい

対処法は、型をそろえることです。

C#
uint a = 10U;
uint b = 3U;

uint result = a + b;

または、必要に応じてlongなどの広い型に変換します。

C#
uint a = 10U;
int b = 3;

long result = (long)a + b;

6-3. 0から1を引くとオーバーフローする

uintは負の値を表現できません。そのため、0から1を引くと、数学的には-1になりますが、uintでは表現できません。

C#
uint value = 0U;

unchecked
{
uint result = value - 1U;
Console.WriteLine(result); // 4294967295
}

uncheckedでは、0 - 1の結果がuint.MaxValueになります。これはオーバーフロー、より正確にはアンダーフローによって値が循環している状態です。

特に、次のような逆順ループは危険です。

C#
// 危険な例
for (uint i = 10U; i >= 0U; i--)
{
Console.WriteLine(i);
}

uintは常に0以上なので、i >= 0Uは常に真になります。その結果、無限ループになる可能性があります。逆順ループではintを使うほうが安全です。

C#
for (int i = 10; i >= 0; i--)
{
Console.WriteLine(i);
}

6-4. checkedコンテキストで安全に計算する方法

オーバーフローを検出したい場合は、checkedを使います。

C#
uint value = 0U;

try
{
checked
{
uint result = value - 1U;
Console.WriteLine(result);
}
}
catch (OverflowException)
{
Console.WriteLine("オーバーフローが発生しました");
}

checkedを使うと、値がuintの範囲を超えたときにOverflowExceptionが発生します。金融、在庫、ポイント、ID管理など、値の不正な循環がバグにつながる処理では、checkedの利用を検討するとよいでしょう。

6-5. uint.MaxValueを超える計算に注意する

uint.MaxValue4,294,967,295です。これを超える計算はオーバーフローします。

C#
uint value = uint.MaxValue;

unchecked
{
uint result = value + 1U;
Console.WriteLine(result); // 0
}

値が大きくなる可能性がある場合は、ulonglongなど、より広い型を使うことを検討しましょう。

C#
uint value = uint.MaxValue;

ulong result = (ulong)value + 1UL;
Console.WriteLine(result); // 4294967296

6-6. 比較演算でintとuintを混在させるときの注意点

intuintを比較するときは、負の値や型変換に注意が必要です。

C#
int a = -1;
uint b = 1U;

Console.WriteLine(a < b); // True

このような単純な比較は理解しやすいですが、複雑な式やキャストが入ると意図しない結果になることがあります。

特に、次のようにキャストすると危険です。

C#
int a = -1;
uint b = (uint)a;

Console.WriteLine(b); // 4294967295

比較や計算でintuintが混在する場合は、どちらか一方の型にそろえる、またはlongなどの広い型に変換してから処理すると安全です。

C#
int a = -1;
uint b = 1U;

long x = a;
long y = b;

Console.WriteLine(x < y); // True

7. uintの使いどころ

7-1. ビット演算やフラグ管理で使う

uintは、ビット演算でよく使われます。32ビットの各ビットに意味を持たせる場合、符号なしのuintは扱いやすい型です。

C#
uint readPermission = 0b_0001U;
uint writePermission = 0b_0010U;
uint executePermission = 0b_0100U;

uint permissions = readPermission | writePermission;

bool canRead = (permissions & readPermission) != 0;
bool canExecute = (permissions & executePermission) != 0;

Console.WriteLine(canRead); // True
Console.WriteLine(canExecute); // False

フラグ管理では、値を「数値」として見るよりも「ビットの並び」として見ることが多いため、uintが適しています。

7-2. 色情報・ARGB・RGBAなどの値を扱う

色を32ビットの値として扱う場合にも、uintが使われることがあります。たとえば、ARGB形式では、A、R、G、Bの各成分を8ビットずつ持つことがあります。

C#
uint color = 0xFF_33_66_99U;
// A = FF, R = 33, G = 66, B = 99

各成分を取り出すには、ビットシフトとビットマスクを使います。

C#
uint color = 0xFF_33_66_99U;

byte a = (byte)((color >> 24) & 0xFF);
byte r = (byte)((color >> 16) & 0xFF);
byte g = (byte)((color >> 8) & 0xFF);
byte b = (byte)(color & 0xFF);

Console.WriteLine(a);
Console.WriteLine(r);
Console.WriteLine(g);
Console.WriteLine(b);

このように、32ビット全体を1つのデータとして扱う場合、uintは自然な選択肢になります。

7-3. 外部APIやファイル形式でUInt32が指定されている場合

外部API、バイナリファイル、通信プロトコルなどで「UInt32」と指定されている場合は、C#側でもuintを使うと意味が一致します。

C#
uint fileSize;
uint checksum;
uint version;

たとえば、ファイル形式の仕様書に「このフィールドは32-bit unsigned integer」と書かれている場合、uintを使うと仕様との対応が明確になります。

ただし、バイナリデータを読み書きする場合は、エンディアン、つまりバイト順にも注意が必要です。

7-4. ハッシュ値やIDなど負数を持たない32ビット値を扱う

ハッシュ値、チェックサム、IDなど、負の数としてではなく32ビットの値として扱いたい場合にもuintが使われることがあります。

C#
uint hash = 0xA1B2_C3D4U;
uint id = 1234567890U;

ただし、C#の標準的なGetHashCode()intを返すため、すべてのハッシュ値にuintを使うという意味ではありません。外部仕様やアルゴリズムがuintを前提としている場合に使うとよいでしょう。

7-5. C言語・C++など他言語との相互運用で使う

C言語やC++のライブラリと連携する場合、関数の引数や戻り値が符号なし32ビット整数で定義されていることがあります。そのような場合、C#側でuintを使います。

C#
// 例: 外部APIが32ビット符号なし整数を要求する場合
uint flags = 0x0000_0001U;

ただし、相互運用ではC/C++側の型サイズを必ず確認する必要があります。unsigned intが常にC#のuintと完全に同じ意味になるとは限らないため、API仕様やプラットフォームの定義を確認しましょう。

7-6. ゲーム開発・低レイヤー処理で使われるケース

ゲーム開発や低レイヤー処理では、uintが使われることがあります。たとえば、ビットフラグ、乱数生成、色データ、メモリ上のデータ表現、GPU関連の値などです。

C#
uint state = 0x1234_5678U;

state ^= state << 13;
state ^= state >> 17;
state ^= state << 5;

Console.WriteLine(state);

このような処理では、数値の正負よりも、ビットパターンそのものが重要になります。そのため、符号なしのuintが向いています。

8. uintを使わないほうがよいケース

8-1. 通常のカウンターやループ変数にはintを使う

通常のカウンターやループ変数には、基本的にintを使いましょう。

C#
for (int i = 0; i < 10; i++)
{
Console.WriteLine(i);
}

uintを使うと、逆順ループやint型のAPIとの組み合わせで問題が起きやすくなります。

C#
// あまりおすすめしない
for (uint i = 0; i < 10U; i++)
{
Console.WriteLine(i);
}

単に「iはマイナスにならないから」という理由だけでuintにする必要はありません。

8-2. 配列やListのインデックスには基本的にintを使う

配列やList<T>のインデックスには、基本的にintを使います。List<T>.Countintです。

C#
var names = new List<string> { "Alice", "Bob", "Charlie" };

for (int i = 0; i < names.Count; i++)
{
Console.WriteLine(names[i]);
}

uintを使うと、Countとの比較やインデックスアクセスで変換が必要になり、コードが読みにくくなることがあります。

C#
var names = new List<string> { "Alice", "Bob", "Charlie" };

uint index = 1U;

// Console.WriteLine(names[index]); // List<T>のインデックスはintなので不便
Console.WriteLine(names[(int)index]);

インデックス用途では、標準APIに合わせてintを使うのが自然です。

8-3. 負の値を「エラー」や「未設定」として扱いたい場合

プログラムでは、-1を「見つからない」「未設定」「エラー」といった意味で使うことがあります。

C#
int index = -1; // 見つからない

uintでは-1を表現できないため、このような設計には向いていません。

C#
uint index = 0; // 0が有効な値の場合、未設定を表しにくい

この場合は、int-1を使うか、uint?のようなnullable型を使う方法があります。

C#
uint? index = null; // 未設定を表す

8-4. int型のAPIと組み合わせる場合は変換が増える

C#や.NETのAPIでは、intが多く使われます。そのため、uintを使うとキャストが増えがちです。

C#
uint count = 10U;

// string text = new string('*', count); // エラー
string text = new string('*', (int)count);

キャストが増えると、コードが読みにくくなるだけでなく、範囲外の値を変換してしまうリスクも増えます。

C#
uint count = uint.MaxValue;

// int value = (int)count; // 危険

標準APIと組み合わせる値は、最初からintにしておくほうが扱いやすいことが多いです。

8-5. 「値がマイナスにならないからuint」は避けるべき理由

uintは、「マイナスにならない値」を表現するためだけの型ではありません。C#では、uintを使うことで次のような問題が起きることがあります。

問題
型変換が増えるintのAPIに渡すたびにキャストが必要
アンダーフローに弱い0U - 1Uuint.MaxValueになる
逆順ループが危険i >= 0Uが常に真になる
エラー値を表現しにくい-1を使えない
意図が伝わりにくいビット演算用なのか通常の数値なのか迷う

基本方針としては、通常の数値にはint、32ビット符号なしであることに意味がある値にはuintを使うと考えるとよいでしょう。

9. uintと他の整数型の比較

9-1. byte・ushort・uint・ulongの違い

byteushortuintulongはいずれも符号なし整数型です。

サイズ範囲
byte8ビット0〜255
ushort16ビット0〜65,535
uint32ビット0〜4,294,967,295
ulong64ビット0〜18,446,744,073,709,551,615

小さなバイナリデータにはbyte、16ビットの外部仕様にはushort、32ビットの符号なし値にはuint、さらに大きな値にはulongを使います。

C#
byte b = 255;
ushort us = 65_535;
uint ui = 4_294_967_295U;
ulong ul = 18_446_744_073_709_551_615UL;

9-2. short・int・longとの違い

shortintlongは符号付き整数型です。

サイズ範囲
short16ビット-32,768〜32,767
int32ビット-2,147,483,648〜2,147,483,647
long64ビット-9,223,372,036,854,775,808〜9,223,372,036,854,775,807

通常の整数にはintintで足りない場合にはlongを使うのが一般的です。負の値を扱う可能性があるなら、uintではなくintlongを選びます。

9-3. nint・nuintとの違い

nintnuintは、ネイティブサイズの整数型です。実行環境が32ビットなら32ビット、64ビットなら64ビットのサイズになります。

符号サイズ
nint符号付き32ビットまたは64ビット
nuint符号なし32ビットまたは64ビット
uint符号なし常に32ビット

uintは常に32ビットですが、nuintは実行環境によってサイズが変わります。nintnuintは、主に相互運用や低レベル処理で使われます。MicrosoftのC#リファレンスでも、nintnuintはネイティブサイズの整数で、プロセスによって32ビットまたは64ビットになると説明されています。

9-4. uintとUInt32はどちらを使うべきか

通常のC#コードでは、uintを使うのが一般的です。

C#
uint value = 100;

UInt32と書いても同じ型です。

C#
UInt32 value = 100;

ただし、UInt32を使う場合は、通常using System;が必要です。

C#
using System;

UInt32 value = 100;

C#のキーワードとして自然に読めるため、普段のコードではuintを使い、ドキュメントやリフレクション、API説明ではUInt32という名前も理解できるようにしておくとよいでしょう。

9-5. 目的別に選ぶC#の整数型一覧

目的別に整数型を選ぶなら、次のように考えるとわかりやすいです。

目的おすすめの型
通常の整数int
大きな整数long
0〜255のバイナリデータbyte
32ビットの符号なし値uint
64ビットの符号なし値ulong
外部仕様でUInt32指定uint
ビットフラグuintまたはenum
配列・Listのインデックスint
未設定を表したい0以上の値uint?またはint

迷った場合は、まずintを選びます。uintを選ぶのは、仕様や用途として「符号なし32ビット整数」であることが重要な場合です。

10. uintでよくあるエラーと解決方法

10-1. 「uintをintに暗黙的に変換できません」の原因

uintintにそのまま代入しようとすると、コンパイルエラーになります。

C#
uint value = 100;
// int number = value; // エラー

uintにはint.MaxValueを超える値が入る可能性があるため、C#は自動変換を許可しません。

解決するには、明示的にキャストします。

C#
uint value = 100;
int number = (int)value;

より安全にするなら、範囲チェックを行います。

C#
uint value = 100;

if (value <= int.MaxValue)
{
int number = (int)value;
Console.WriteLine(number);
}

10-2. 「定数値をuintに変換できません」の原因

uintの範囲外の定数を代入するとエラーになります。

C#
// uint value = -1; // エラー

uintは負の値を扱えません。負の値が必要な場合はintを使います。

C#
int value = -1;

また、uint.MaxValueを超える値も代入できません。

C#
// uint value = 4_294_967_296U; // エラー

この場合は、ulongなどの大きな型を使います。

C#
ulong value = 4_294_967_296UL;

10-3. uintとintの演算結果を代入できない場合

uintintを混ぜた演算結果をuintに代入しようとすると、エラーになることがあります。

C#
uint a = 10U;
int b = 5;

// uint result = a + b; // エラーになりやすい

解決方法は、型をそろえることです。

C#
uint a = 10U;
uint b = 5U;

uint result = a + b;

または、広い型で受け取ります。

C#
uint a = 10U;
int b = 5;

long result = (long)a + b;

intuintが混在するコードは、型変換のルールを理解していないと読みにくくなります。可能な限り、同じ意味を持つ値は同じ型で統一しましょう。

10-4. ParseでFormatExceptionが発生する場合

uint.Parseに数値ではない文字列を渡すと、FormatExceptionが発生します。

C#
string text = "abc";

// uint value = uint.Parse(text); // FormatException

ユーザー入力では、TryParseを使うのがおすすめです。

C#
string text = "abc";

if (uint.TryParse(text, out uint value))
{
Console.WriteLine(value);
}
else
{
Console.WriteLine("数値として変換できません");
}

空文字、文字列、記号、小数なども変換に失敗します。

C#
uint.TryParse("", out _);       // false
uint.TryParse("abc", out _); // false
uint.TryParse("1.5", out _); // false
uint.TryParse("-1", out _); // false

10-5. オーバーフロー例外が発生する場合

checkedコンテキストでuintの範囲を超える計算や変換を行うと、OverflowExceptionが発生します。

C#
try
{
checked
{
uint value = uint.MaxValue;
uint result = value + 1U;
Console.WriteLine(result);
}
}
catch (OverflowException)
{
Console.WriteLine("uintの範囲を超えました");
}

文字列から変換する場合も、uintの範囲を超えると例外が発生することがあります。

C#
string text = "4294967296";

// uint value = uint.Parse(text); // OverflowException

この場合も、TryParseを使うと安全です。

C#
string text = "4294967296";

if (uint.TryParse(text, out uint value))
{
Console.WriteLine(value);
}
else
{
Console.WriteLine("uintの範囲外です");
}

10-6. よくあるエラーをサンプルコードで確認

uintでよくあるエラーと対処法をまとめると、次のようになります。

C#
// 1. 負の値は代入できない
// uint a = -1;
int a = -1;

// 2. uintからintへは暗黙変換できない
uint b = 100U;
// int c = b;
int c = (int)b;

// 3. 大きすぎる値はintに入らない
uint d = 4_000_000_000U;
if (d <= int.MaxValue)
{
int e = (int)d;
}

// 4. 0から1を引くと危険
uint f = 0U;
unchecked
{
uint g = f - 1U;
Console.WriteLine(g); // 4294967295
}

// 5. 文字列変換はTryParseが安全
string text = "123";
if (uint.TryParse(text, out uint h))
{
Console.WriteLine(h);
}

uintを使うときは、範囲、符号、型変換、オーバーフローの4点を意識するとエラーを避けやすくなります。

11. 初心者向けQ&A

11-1. uintは何の略ですか?

uintは「unsigned int」の略です。日本語では「符号なし整数」と呼ばれます。

C#のuintは、0〜4,294,967,295の範囲を持つ32ビット符号なし整数型です。.NETの型名ではSystem.UInt32です。

11-2. uintとintはどちらを使えばよいですか?

通常はintを使えば問題ありません。C#の標準APIではintが多く使われるため、ループ変数、カウンター、インデックス、一般的な整数値にはintが向いています。

uintを使うのは、次のような場合です。

uintが向いている場面理由
ビット演算32ビットの並びとして扱いやすい
色情報ARGBやRGBAを32ビット値で表せる
外部仕様でUInt32指定仕様と型を一致させられる
C/C++連携符号なし32ビット整数に対応しやすい
チェックサムやID負数ではなく32ビット値として扱える

「マイナスにならないから」という理由だけなら、intのままでよいことが多いです。

11-3. uintは小数を扱えますか?

uintは整数型なので、小数は扱えません。

C#
// uint value = 1.5; // エラー

小数を扱いたい場合は、floatdoubledecimalを使います。

C#
double value = 1.5;
decimal price = 123.45m;

個数や回数のような整数にはintuint、割合や金額のような小数にはdoubledecimalを使い分けましょう。

11-4. uintにnullは入れられますか?

通常のuintにはnullを入れられません。uintは値型だからです。

C#
// uint value = null; // エラー

nullを扱いたい場合は、nullable型のuint?を使います。

C#
uint? value = null;

if (value.HasValue)
{
Console.WriteLine(value.Value);
}
else
{
Console.WriteLine("値がありません");
}

uint?は、「値がある場合は0以上の整数、値がない場合はnull」という表現ができます。

11-5. uintは負の数を絶対に扱えませんか?

通常の意味では、uintは負の数を扱えません。

C#
// uint value = -1; // エラー

ただし、キャストによって負のintuintに変換することはできます。

C#
unchecked
{
int a = -1;
uint b = (uint)a;

Console.WriteLine(b); // 4294967295
}

これは「負の数を保持している」のではなく、同じビット列をuintとして解釈した結果です。初心者のうちは、負の値をuintにキャストするコードは避けるのが無難です。

11-6. uintはパフォーマンス面でintより有利ですか?

通常のアプリケーション開発では、uintintより明確に高速になることはほとんどありません。どちらも32ビット・4バイトであり、一般的な計算では大きな差を意識する必要はありません。

むしろ、uintを使うことでintとの変換が増え、コードが複雑になる場合があります。パフォーマンス目的でuintを選ぶのではなく、データの意味や外部仕様に合わせて選ぶのが基本です。

低レイヤー処理、ビット演算、ゲーム開発、相互運用などではuintが自然な選択になることがありますが、通常の業務ロジックではintを使うほうが扱いやすいです。

まとめ

C#のuintは、0〜4,294,967,295の範囲を扱える符号なし32ビット整数型です。System.UInt32の別名であり、intと同じ4バイトのサイズを持ちます。

intとの主な違いは、負の値を扱えるかどうかです。intは符号付きで、負の値から正の値まで扱えます。一方、uintは符号なしで、0以上の値だけを扱う代わりに、intより大きな正の値を表現できます。

ただし、C#では通常の整数にはintを使うのが一般的です。uintは、単に「マイナスにならない値」だから使うのではなく、ビット演算、色データ、外部API、ファイル形式、C/C++との相互運用など、32ビット符号なし整数であることに意味がある場合に使うのが適しています。

uintを使うときは、次のポイントを押さえておきましょう。

ポイント内容
範囲0〜4,294,967,295
サイズ32ビット・4バイト
.NET型System.UInt32
負の値直接は扱えない
通常の整数基本はintを使う
型変換intとの変換には注意
計算オーバーフローやアンダーフローに注意
主な用途ビット演算、UInt32指定の外部仕様、色データ、相互運用

初心者のうちは、「普通の数値ならint、32ビット符号なしである必要があるならuint」と覚えておくと、型選びで迷いにくくなります。