この記事では、当社の新人エンジニア研修の参考にC#を解説します。
前回はC#言語の特徴などを解説しました。
今回は、変数とデータ型について解説します。
変数とは?
変数は「データに付ける名前(ニックネーム)」だと考えてください。変数を使うことで、データの再利用や演算が容易になります。
プログラミングに限らず、「名前」という仕組みがないと非常に不便ですよね。現実でも、「クリスマスイブの日に橋の下に捨てられていた白い犬」を毎回その長い呼び名で呼ぶのではなく、「三太」と名前をつければ簡単に指し示すことができます。これが変数の役割とよく似ています。
設計において、適切な名前をつけられるかどうかは非常に重要な要素
(Ruby開発者:まつもとゆきひろ 〜「プログラマが知るべき97のこと」より)
コンピュータの本質は「計算機」です。計算機はデータを処理するため、一時的にデータを保管する場所が必要です。その場所に「名前」を付けて扱いやすくしたものが変数です。
例えば、メインメモリ上のアドレスをそのまま使うのは人間には分かりにくいです。そこで、「わかりやすい名前(変数名)」を付けてあげることで、プログラム中で扱いやすくしています。
変数を「引っ越しの荷物を入れるダンボール箱」になぞらえることがあります。
ダンボール箱にラベルがないと、中身が何か分からず困ります。同様に、変数にも「何を入れるか」の名前付けが重要なのです。
また、箱ごとに「食器用」「電化製品用」のように用途を決めておけば混乱しません。同様に、変数に何を入れるかをあらかじめ決めるのがデータ型です。データ型を決めることで、間違ったデータ操作を防ぎます。
- 例:
2 + 3
は5
という数値演算"Hello " + "World"
は"Hello World"
という文字列結合- 数値同士であれば加減乗除が可能ですが、文字列との演算は結果が異なる、もしくは意味をなさない場合もある
C#でも整数値や文字列、真偽値など様々な型を扱い、また自分たちで作る型(クラス)も登場します。データ型を理解することは、C#を使いこなすうえで欠かせません。
「変数(Variable)」の「変わる」という言葉通り、プログラム中で値が変化する可能性があります。それに対して、変化しない(変えられない)ものを「定数(Constant)」と呼びます。ここではまず変数を、続いて定数を学んでみましょう。
1. 変数とは
以下のサンプルプログラム(C#版 Example05
)では、型の異なる4つの変数を宣言し、それぞれの値をコンソールに出力します。
using System;
public class Example05
{
public static void Main(string[] args)
{
int data1 = 1;
double data2 = 1.0;
bool flag = false;
char c = 'C';
Console.WriteLine(data1);
Console.WriteLine(data2);
Console.WriteLine(flag);
Console.WriteLine(c);
}
}
<結果>
1
1.0
False
C
C#のプログラムも、Java同様「上から下へ」順次実行されます。
- 変数を 宣言 する:
int data3;
- 値を 代入 する:
data3 = 10;
- 宣言と代入を一行で書く:
int data3 = 10;
- 変数を使う(参照 する):
Console.WriteLine(data3);
誤りが多いのは、Console.WriteLine("data3")
のように、変数名を文字列リテラルで書いてしまうことです。そうすると、画面出力は "data3"
という文字そのものになります。
実験
「もし、文字列を渡すと実行結果はどうなるか?」
Console.WriteLine("data1");
→ 画面には data1
と表示され、変数data1
の中身は出力されません。
また、他の変数の値を代入することもできます。
int data4 = data3;
そして、演算の結果を代入することもできます。
data4 = data3 + 1;
変数に新しい値を代入すると、もともと入っていた値は上書きされます。
data3 = data3 + 1;
例題
int a = 1;
int b = a;
a = 2;
Console.WriteLine(a + ":" + b);
この結果はどうなるでしょう? その理由は?
b
にa
の値をコピーしたタイミングを考えると、b
は1
のままです。後でa
が2
になってもb
は変化しません。
ローカル変数
今回紹介した変数はローカル変数です。メソッド内の { }
ブロックの範囲だけで有効となります。
また、C#ではローカル変数を初期化しないまま使おうとするとコンパイルエラーになります。
int data1;
Console.WriteLine(data1); // ← ここで使おうとするとエラー
「変数は初期化してから使う」というのが原則です。
直接記述される定数値のことを「リテラル」と呼びます。例えば
1
,1.0
,false
,'C'
はすべてリテラルです。
2. 変数の宣言
C#では、変数を宣言するときに「型」を先に書きます。これは静的型付け言語の特徴です。
型名 変数名;
- C#での「名前(変数・メソッド・クラスなど)」を識別子と呼びます。
- 識別子には、英字と数字、アンダースコア
_
などが使えますが、先頭を数字にしてはいけません。
たとえば、int 1stNumber;
のような宣言はできませんし、C#にも予約語があり、それらを変数名には使えません。
int int = 0; // これはエラー
このような宣言ができてしまうと、int
が型なのか変数なのか区別がつかなくなるため禁止されています。
予約語とリテラル
C#にもたくさんの予約語(public
, class
, if
, static
など)やリテラル(true
, false
, null
など)があります。Visual StudioやVS CodeなどのIDEで色分けされるので、それを目安にしてください。
実験:同じ変数名を2回宣言してみる
int data1 = 10;
int data1 = 20; // 再度宣言するとどうなるか?
→ 「変数が重複している」エラーが出るはずです。エラーメッセージをメモしておきましょう。
実験:変数の参照と宣言の順番を逆にしてみる
Console.WriteLine(data1);
int data1 = 10;
→ 変数が見つからない旨のエラーが出るでしょう。C#もJava同様、「基本は上から下に処理する言語」だからです。
静的型付けと動的型付け
- 静的型付け:C#、Java、C++、TypeScript など
- 動的型付け:Python、JavaScript、Ruby、PHP など
どちらも一長一短がありますが、最初に学ぶ言語としては、コンパイル時に型エラーを検出しやすい静的型付け言語がバグを減らせる分だけ勉強しやすい、という考え方もあります。
3. C#で使用できる主な組み込み型(プリミティブ型に相当)
Javaには8つのプリミティブ型がありましたが、C#では概念上似たものを含め、下記の組み込み型があります。Javaの「基本データ型(プリミティブ型)」に相当します。
型 | ビット幅 | 値の範囲・内容 |
---|---|---|
bool | — | true または false |
byte | 8ビット | 符号なし(※) / 符号あり sbyte が別にある |
char | 16ビット | Unicode 文字 (U+0000 ~ U+FFFF) |
short | 16ビット | -32768 ~ 32767 |
int | 32ビット | -2,147,483,648 ~ 2,147,483,647 |
long | 64ビット | -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 |
float | 32ビット | 単精度浮動小数点数(-3.4×10^38~3.4×10^38 など) |
double | 64ビット | 倍精度浮動小数点数(±5×10^-324~±1.7×10^308 など) |
decimal | 128ビット | 高精度(財務計算などで使用) |
Javaとの最大の違いとして、C#には
decimal
や符号なし整数型 (uint
,ushort
,ulong
など) も用意されています。
メモリ上の表現
int
の 1 はメモリ上で「32ビットの2進数」で表されるdouble
の 1.0 はまた別の2進数表現
「同じ 1
でも、整数の1と実数の1.0 はメモリ上のビットパターンが異なる」という点はJava同様です。この違いが「型変換(キャスト)」で重要になってきます。
4. なぜ、変数を使うのか?
変数を使う理由を一言で言うと、「データに意味を与え、使い回し、変更ができる」からです。
以下のExample06
は、あなたが現在22歳だとして、将来の年齢をコンソールに表示するプログラムです。
using System;
public class Example06
{
public static void Main(string[] args)
{
int yourAge = 22;
Console.WriteLine("あなたは今" + yourAge + "才です");
Console.WriteLine("あなたは10年後" + (yourAge + 10) + "才です");
Console.WriteLine("あなたは20年後" + (yourAge + 20) + "才です");
Console.WriteLine("あなたは30年後" + (yourAge + 30) + "才です");
Console.WriteLine("あなたは40年後" + (yourAge + 40) + "才です");
Console.WriteLine("あなたは50年後" + (yourAge + 50) + "才です");
}
}
<結果>
あなたは今22才です
あなたは10年後32才です
あなたは20年後42才です
あなたは30年後52才です
あなたは40年後62才です
あなたは50年後72才です
yourAge
という「年齢」だと分かりやすい名前が付いています。これが数値22
のままだと「何を表す数字か?」が不明瞭です。- 年齢が1歳増えた場合は、単に
yourAge = 23
と書き換えれば良いですね。
このように、変数を使うことでデータの意味付け、使い回し、変更が容易になります。
変数名における キャメルケース (camelCase) :
yourAge
,myFriendName
など
C#のローカル変数では小文字始まりが一般的です(ただし意見は分かれる場合あり)。
5. 定数を使うとき
変数は後から値が変わる可能性があります。それに対して「後から変更できない」ものを定数と言います。C#ではconst
キーワードを使います。
public class Example07
{
public static void Main(string[] args)
{
const int ADULT_AGE = 20;
// ADULT_AGE = 18; // ← これはコンパイルエラー: 値を変更できない
}
}
定数は宣言時に初期化する必要があり、以降の再代入はできません。
public class Example08
{
public static void Main(string[] args)
{
const int ADULT_AGE = 18;
Console.WriteLine("成人年齢は" + ADULT_AGE + "才です");
Console.WriteLine("今年の成人は10年後" + (ADULT_AGE + 10) + "才です");
Console.WriteLine("今年の成人は20年後" + (ADULT_AGE + 20) + "才です");
Console.WriteLine("今年の成人は30年後" + (ADULT_AGE + 30) + "才です");
Console.WriteLine("今年の成人は40年後" + (ADULT_AGE + 40) + "才です");
Console.WriteLine("今年の成人は50年後" + (ADULT_AGE + 50) + "才です");
}
}
<結果>
成人年齢は18才です
今年の成人は10年後28才です
今年の成人は20年後38才です
今年の成人は30年後48才です
今年の成人は40年後58才です
今年の成人は50年後68才です
Javaではfinal
修飾子をつけ、大文字+アンダーバーのスネークケース(ADULT_AGE
)で書く慣習ですが、C#でも同様に定数は全大文字+アンダースコアというスタイルがよく使われます。
6. char
型は整数のように扱える?
次のExample09
を見てください。
public class Example09
{
public static void Main(string[] args)
{
char a = 'A';
a += (char)32;
Console.WriteLine(a);
}
}
<結果>
a
C#のchar
型も内部的には「文字コード(16ビットの数値)」として扱われます。 'A'
のコードに 32
を足すと 'a'
になるわけです。(ASCIIテーブルなどを参照)
文字の大小変換や暗号化などで、char
を数値的に操作するテクニックが使われる場合があります。
7. クラスはnamespaceを使って管理する
Javaでは「package」でクラスを整理していましたが、C#では「namespace」を使います。
今までのサンプルがすべてデフォルト名前空間(namespaceなし)だったとすると、クラスが増えると整理がしにくくなります。現場ではnamespaceを使ってソースコードを分類します。
namespace Chap02
{
public class Example01
{
public static void Main(string[] args)
{
char a = 'A';
a += (char)32;
Console.WriteLine(a);
}
}
}
このように記述することで「Chap02」という名前空間配下にExample01
クラスが属することになります。同じソリューション(プロジェクト)内でクラス名の衝突を防ぐ効果もあります。
8. 別のnamespaceにあるクラスを呼び出して使用する
Javaの java.time.LocalDateTime
に相当するC#の日時クラスは System.DateTime
です。
namespace Chap02
{
public class Example02
{
public static void Main(string[] args)
{
// 完全修飾名で呼び出し
Console.WriteLine(System.DateTime.Now);
}
}
}
<結果>
2025/02/18 16:45:55
(表示形式はPCのロケールや設定によります)
さらに、using System;
と書いておけば、単に DateTime.Now
と書けます。
using System;
namespace Chap02
{
public class Example03
{
public static void Main(string[] args)
{
Console.WriteLine(DateTime.Now);
}
}
}
Javaのimport
に近い機能が、このC#の using
文です。ただし誤解しがちですが、using
は「クラスを読み込む」わけではなく「名前空間を省略できる」ようにする仕組みです。
using System;
using System.Text;
// など
と書いておくと、その名前空間配下のクラスを完全修飾名なしで呼び出せるようになります。
using System.Text;
でStringBuilder
クラスをStringBuilder sb = new StringBuilder();
のようにシンプルに書ける- Javaでの
import java.time.*;
相当としてusing System.Text;
のようにワイルドカードは使えません(C#にはusing System.Text.*;
といった書き方はありません)。
C#の
System.*
は標準ライブラリが格納されている場所であり、Javaのjava.lang
やjava.util
に近いイメージです。
<まとめ:隣の人に正しく説明できたらチェックを付けましょう>
- 文字型はシングルクォーテーション
' '
で囲い、文字列はダブルクォーテーション" "
で囲うのは、変数名などと区別するためである。 - 変数を作成することを「変数の宣言」といい、値を入れることを「代入」、変数の中身を見ることを「参照」という。
- 変数には他の変数の値を代入でき、また演算の結果を代入することもできる。
- ローカル変数はスコープが最も狭く、初期化してから使用する。
- 変数の宣言では、あらかじめデータ型を指定する(静的型付け)必要がある。
- まずは
bool
,int
,double
(Javaのときはboolean
,int
,double
)の3つあたりを押さえよう。 - namespace(Javaのpackage)を使う目的は (1)分かりやすい分類整理 (2)名前空間の提供 (3)不要なものを公開しない(アクセス制限) の3つ。
まとめができたら、実際にコードを書いてみて、自分で試行錯誤しながら習得を進めましょう。
変数とデータ型はあらゆるプログラミング言語の基礎です。ここでしっかり理解しておくと、後のオブジェクト指向や高度なライブラリ操作を学ぶときに大いに役立ちます。