この記事では、当社の新人エンジニア研修の参考にC#を解説します。
第一章となる今回はC#言語を初めて学ぶ皆さんが知っておきたいことをまとめました。
なぜ、C#を学ぶのか、その理由。
1.C#言語の特徴
C#言語の特徴(この新人エンジニア研修でC#を選択している理由とも言えます)は、以下の4点です。
- WindowsやLinux、macOSなど、.NETランタイム(.NET Runtime)やMono、.NET Coreなどを利用することで幅広く実行可能。自宅学習やクロスプラットフォーム開発にも対応できる。
- 現在主流のプログラミングパラダイムであるオブジェクト指向型言語の基本を学ぶことができる。
- エンタープライズアプリケーション、ゲーム開発(Unity)、ウェブアプリケーション(ASP.NET)、クラウドサービスとの連携など、幅広い分野で活用されている。どのような部署に配属されても役に立つ。
- 多くの言語(C/C++/Javaなど)と類似点が多いため、次に他の言語を学ぶ際に容易に習得できる。
例えば、汎用プログラミング言語として名高いC言語を選択したとすると、上記すべてのメリットを同時に得るのは難しいかもしれません。AI分野で注目度が高いPython、国内発祥のRuby、Apple製品で人気が高まっているSwiftなどの言語も魅力的ではありますが、3の部分(大規模開発やWindowsアプリなどでの優位性)では、まだC#に一日の長があると言っていいでしょう。
ということで、当社の新人エンジニア研修ではプログラミング言語としてC#を使うことが最も多いです。
調べてみましょう
4の他の言語との関係について概略は以下のとおりです。将来使うかもしれない言語について更に詳しく調べてみましょう。
1. C#とC言語との関係
関係
- 歴史的背景: C言語は1970年代に開発された歴史のある言語です。C#はC言語の構文や記法から多くの影響を受けており、制御構造(
if
文、for
文、while
文など)のベースが似ています。 - 構文: C#の構文はC言語に近く、基本的な文法が共通していますが、C#はオブジェクト指向を前提とした構造になっています。
習得の容易さ
- C#の基本的な文法や概念を理解していれば、C言語の文法自体は比較的容易に理解できます。
- C言語は手続き型言語であり、メモリ管理を直接行う部分があるため、C#のようなガベージコレクションに慣れていると戸惑うかもしれませんが、基本的なプログラミング概念は共通しています。
- C#ではメモリのことをあまり意識しなくてもいいですが、メモリに興味をもってC言語を学ぶと、よりシステムレベルの知識が深まります。
分野
- C言語: システムプログラミング、組み込みシステム、低レベルのハードウェア制御、OS開発など、メモリを直接操作する分野で存在感があります。
2. C#とC++との関係
関係
- 歴史的背景: C#はC++やJavaから強く影響を受けており、C++の複雑さを改善する目的で設計された面があります。
- 構文: C#の構文はC++と非常に似ており、オブジェクト指向プログラミング(OOP)の概念も共通しています。
習得の容易さ
- C#の基本的な文法や概念を理解していれば、C++の文法も比較的容易に理解できます。
- OOPの概念(クラス、オブジェクト、継承、ポリモーフィズムなど)は両言語で共通しているため、C#のOOPの知識はC++の学習に役立ちます。
分野
- C++: ゲーム開発、高性能システム、デバイスドライバ、組み込みシステムなど。ネイティブコードの高速性が求められる領域で活躍しています。
3. C#とJavaとの関係
関係
- 歴史的背景: C#はMicrosoftによって開発され、Javaの影響を非常に強く受けています。
- 構文: C#の構文はJavaとよく似ており、多くの文法要素がほぼ同じ書き方で使えます。
習得の容易さ
- Javaの知識があれば、C#の基本的な文法や構造をすぐに理解できます。
- 両言語は.NETランタイム(C#)とJava仮想マシン(JVM)のように、似たコンセプトのバイトコード実行環境を持っています。
分野
- C#: Windowsアプリケーション、ゲーム開発(Unity)、ウェブアプリケーション(ASP.NET)、エンタープライズソフトウェアなど、広範囲に活用されています。
4. C#とPythonとの関係
関係
- 構文とスタイル: Pythonの構文はC#と異なりますが、基本的なプログラミング概念(変数、関数、ループ、条件分岐など)は共通しています。
- オブジェクト指向: Pythonもオブジェクト指向プログラミングをサポートしており、C#のOOP知識が活かせます。
習得の容易さ
- C#で学んだ基本的なプログラミング概念やアルゴリズムの知識は、Pythonにも直接適用できます。
- PythonはC#より文法が簡潔で、初心者でも習得しやすいです。先にC#をしっかり学んでおけば、Pythonも比較的スムーズに習得できるでしょう。
分野
- Python: データサイエンス、AI・機械学習、自動化スクリプト、科学計算などで利用されることが多いです。
5. C#とJavaScriptとの関係
関係
- 名前の混同: JavaScriptとJavaは名前が似ていますが別物であり、C#とも構文や実行環境が異なります。ただし、C#もJavaScriptの非同期処理やイベント駆動型の考え方とは違った部分があるため、学ぶ価値は十分にあります。
- 構文と概念: JavaScriptの構文はC#とは異なりますが、制御構文には共通点も多くあります。
習得の容易さ
- C#で培ったプログラミングの基礎知識やロジックの考え方は、JavaScriptの学習にも役立ちます。
- JavaScript特有の非同期処理やイベント駆動型プログラミングはC#の知識がベースになっていれば理解が進みやすいかもしれません(C#にも
async/await
があります)。 - MicrosoftがJavaScriptを拡張して作ったTypeScriptも狙い目です。TypeScriptは静的型付けやオブジェクト指向をサポートし、C#に近い記述体験ができます。
分野
- JavaScript: フロントエンド開発(ブラウザ側の処理)、サーバーサイド開発(Node.js)、モバイルアプリ開発(React Native, Ionic)など。
2.C#プログラムの実行
C#でプログラムコードが実行されるまでには、以下のような2つのステップを経ます。
- プログラムコードがコンパイルされて、中間言語(IL: Intermediate Language)コードが生成される。
- CLR(Common Language Runtime)によってILコードが実行される。
これが例えばC言語の場合ですと、
- プログラムコードがアーキテクチャ(OS等)に応じた形でコンパイルされてマシン語が作られる。
- マシン語が実行される。
という流れになっています。
つまり、下図のようにCLRが各種OSとの仲立ちをしてくれるため、C#プログラマーはアーキテクチャを意識せずにコードを記述しやすくなっているのです。
新人エンジニアが最初に知っておきたいCLRの知識
CLRがOSの違いを吸収してくれます。
C#は、
“Write once, run anywhere”
「一度コードを書けば、どこでも実行できる」
(.NETやMonoなどのクロスプラットフォーム対応によって)
という優れた特徴を有していると言えます。(※実際には対応環境を揃える必要があるので、完全に意識不要とはいきませんが、Java同様に移植性が高いです。)
以下のプログラムを講師の指示に従ってエディタに入力してみましょう。
using System;
public class Example01 {
public static void Main(string[] args) {
Console.WriteLine("Hello World");
}
}
キーボードから入力できる文字には全角文字と半角文字があります。数値や英字・記号を入力する際には全角文字が入らないように気をつけてください。私は、全角の英数字や記号が入らないように日本語入力システム(IME)の設定を変えています。IMEによって設定方法が異なるので、興味があれば調べてみてください。
入力したら、講師の指示に従って(Cドライブ直下に作成したCSharpフォルダなどに)新規保存します。
その際のファイル名は、「Example01.cs」です。
コマンドラインでコンパイルして実行してみます。(環境によってはVisual StudioやVS Code、.NET CLIを使う方法もあります)
> csc Example01.cs ←①
> Example01 ←② (WindowsならExample01.exeでも可)
Hello World ←③
- プログラムコードがコンパイルされてILコードが作られる
- ILコードがCLRによってメモリにロードされ、実行される
- 実行結果が表示されました
①のところでは拡張子.cs
を付けたのに対して、②では拡張子をつけずに実行している点に注意してください。(Windowsの場合は.exe
ファイルが生成され、それを実行しています)
画面に「Hello World」と表示されたと思います。
※なお、Example01.cs
というソースプログラムと、Example01.exe
(またはExample01.dll
)が作られています。どこに作られたか、講師の指示の下で探してみてください。
誤解が多いのですが、このプログラムコード(ソースファイル)自体が直接「動作」しているわけではありません。現在のノイマン型コンピュータでは、ソースコードをコンパイルした結果(ILコードやマシン語)がメモリにコピー(ロード)されて動きます。この点が、後で重要になってきます。
なお、コマンドラインでコンパイルするのは手間がかかる場合もあるので、実際の開発ではVisual StudioやVS Codeなどの統合開発環境(IDE)を使います。ショートカットキーひとつでコンパイルと実行を同時にしてくれるので便利です。
(ちなみに「compile」は「一気に翻訳する」という意味です。それとは別に「interpret」「1文ずつ通訳する」方式のスクリプト言語もあります。気になる方は調べてみましょう。)
ここまでに出てきた用語をまとめます。
覚える必要はないですが、下図で概要を理解してください。
まず、みなさんがソースコードを入力するのがIDEです。IDEは.NET SDK(C#開発環境)を利用しています。.NET SDKの中にはC#コンパイラ(Roslynなど)や実行環境(.NET Runtime)などが含まれます。Javaで言う「JRE」「JDK」「JVM」にあたるものが、.NETでは「.NET Runtime」「.NET SDK」「CLR(Common Language Runtime)」に相当します。
これからIDEを使って開発をしていると、これら“縁の下の力持ち”の存在を忘れがちですが、時々思い出すようにしましょう。ここでは、さらに詳しくC#言語のプログラムコードを見ていきます。
3.C#プログラムの構成
先ほどのExample01
をIDEに入力して実行してみましょう。
using System;
public class Example01
{
public static void Main(string[] args)
{
Console.WriteLine("Hello World");
}
}
実行の方法はIDEごとに違いますので講師から説明します。
1行目の
using System;
は、C#の標準ライブラリであるSystem
名前空間を使う宣言です。Javaで言うimport
文に近いイメージですが、記述ルールはC#独自のものになります。
次に
public class Example01
{
...
}
という部分でクラスを定義しています。
C#のクラスとは「モノの設計図(テンプレート)」です。周りを見渡しても工業製品で設計図なしに作られるものはありません。あるいは人間などの生物も遺伝子という一種の設計図を元に体が作られているのは周知の事実です。C#では、この“モノ”のことを「オブジェクト」と呼びます。
つまり、オブジェクト指向とは、プログラムを「クラス」の集合体とみなしましょう、という考え方です。
4.オブジェクト指向のメリットとデメリット
オブジェクト指向とは、プログラムに必要なデータと処理の整理術です。
データと処理を、現実のモノと同じように1つのクラスにまとめて整理しよう、という考え方です。もともとデータと手続きを分離するプログラミング方法ではメンテナンスが難しい場合があり、それを補うために誕生したパラダイムと考えると分かりやすいです。
C#では、クラスを複数作り、それらを組み合わせてプログラム全体を構築します。
そもそもなぜオブジェクト指向が生まれたかというと、それは「メンテナンスしやすいプログラムを作るため」です。
開発に多くの予算を使ったシステムは、業務変更や法律の改正などにともない長く使い続けたいものです。そのときに、モノの世界はどうしているかを考えてみましょう。たとえばパソコンはパーツが古くなったらパーツだけ交換できますよね。一部分を変更しても全体には影響しないようになっている。それと同じような設計思想をソフトウェアにも活かそう、というのがオブジェクト指向の根幹です。
ただしデメリットとしては、最初に作るときにあれこれ設計を考慮しなくてはいけないため、学習コストが高く感じられるかもしれません。「とりあえず動けばいい」だけで済ませる場合は、オブジェクト指向の真価は発揮されにくいのです。そのため新人エンジニア研修の短い期間内では、メリットを十分に体感できないかもしれません。
もし「オブジェクト指向が有効な事例を1つ挙げよ」と問われたら、私はC#言語自体を挙げるでしょう。C#は2000年代初頭から現在に至るまで、時代の変化とともに要求される技術要素を進化させながら後方互換を保っています。(古いバージョンとの互換性) これはオブジェクト指向言語としての拡張のしやすさを証明しているといえます。
5.本記事の方針
クラスには、自分たちで作るもののほか、.NET標準ライブラリ(Javaでいう標準APIのようなもの)として、Microsoftやコミュニティがあらかじめ作ってくれているものがあります。また、サードパーティーのライブラリや自社で開発したライブラリなど、世の中には膨大なクラスが存在します。
プログラムでやりたいことがある場合、まず「既存のクラスやライブラリを活用できないか」を考えてください。なぜなら、自作クラスにはバグが入り込む可能性があるからです。多くの人が利用している実績あるクラスのほうが信頼度が高いケースも多いのです。
※なんでも自作にこだわることを「車輪の再発明」と言って戒めることがあります。
ここで、本記事の方針を説明します。
この記事では、
- 「C#のことはC#に訊け」という精神で、標準ライブラリやそのソースコードを例に解説します。
- 当社が受講者に課す最終課題をやり抜く上で必要となるクラスを中心に紹介します。
- プログラミング言語は国際語であるという点を踏まえて英語の意味から解説します。
ただし、C#のすべてを網羅しているわけではありませんのでご了承ください。
1.「C#のことはC#に訊け」
みなさんには3つの「武器」を紹介します(下図1.3のイメージ)。
- .NET標準ライブラリ(公式ドキュメント/リファレンス)
- .NET標準ライブラリのソースコード(オープンソースとして公開されている部分もあります)
- サンプルプログラム
たとえば英語学習では、英和辞典や例文集を使いますね。人によっては英英辞典を使ったかもしれません。
- .NET標準ライブラリは「英和辞典」です。分からないクラスがあればリファレンスや公式ドキュメントを参照しましょう。
- .NET標準ライブラリのソースコードは「英英辞典」です。C#がC#自身で書かれているソースを読むのは非常に有用です。
- サンプルプログラムは特に覚えて損のない表現を掲載しました。イディオムとして覚えていきましょう。
C#も1つの言語なので、習得には時間がかかります。じっくり付き合うつもりでいてください。とはいえ、語学と違い、C#で覚える“文法”や“単語”の数はそう多くはありません。むしろ、概念の理解こそが大切です。理解すれば記憶も身につきます。暗記に頼りすぎないようにしましょう。
6.クラスのメンバ(フィールドとメソッド)
※以下は少し高度な論点です。詳しくはこの連載全体を通じてお伝えしますが、ここでも簡単に触れておきます。
すべてのソースコードはクラスに属する必要があります。クラスの識別のためにはクラス名が必要で、クラス名は自由につけられますが、慣習として先頭を大文字にします(PascalCase)。例:Example01
下図のとおり、クラスはフィールド(field)とメソッド(method)を持つことができます。
フィールドとメソッドをあわせてメンバと呼ぶ
- フィールド: 「場」を表し、データやオブジェクトの置き場所のこと。
- メソッド: 「方法」を表し、処理や操作のこと。
たとえば、新入社員オブジェクトであれば誰でも「プログラミング経験年数」や「所有パソコン」といったフィールドを持ち、また「挨拶をする」「プログラムを書く」などのメソッドを持っていると考えられます。
C#のメソッドの基本書式は以下の通りです。
[修飾子] 戻り値の型 メソッド名(引数1の型 引数1, 引数2の型 引数2, …)
{
// メソッドの処理
}
{}
ブロックの中に命令文を書きます。{
と}
の対応漏れは初学者に多いミスです。気をつけましょう。
Main()
メソッドは、C#で特別な意味を持つメソッドです。CLRがプログラム実行時に最初に探す「エントリーポイント」(開始位置)となります。必ずどこかのクラスに1つはMain
メソッドが必要です。
Main(string[] args)
という形で引数を持ちます。ここでargs
は仮引数です。実際の引数はコマンドラインから渡せますが、ほとんど使わない場合でもこの形が基本です。
public static void Main(string[] args)
{
...
}
public
、static
、void
は修飾子や戻り値の型にあたります。public
はアクセス修飾子(公開範囲)で、外部からでも呼び出せることを示します。static
はインスタンスを生成しなくても呼び出せるメソッドであることを示します(静的メソッド)。void
は戻り値がないことを意味します。
Console.WriteLine(...)
のようなメソッド呼び出しには、オブジェクト指向のエッセンスが詰まっています。私たちはメソッド名と引数リスト、そして戻り値の型さえ知っていれば、そのメソッドを呼び出せるのです。中身の仕組みを知らなくても利用できる、この抽象化(ブラックボックス化)こそがオブジェクト指向の特徴です。
7.エラーを恐れない
では、少し実験してみましょう。以下のコードを実行するとどうなるでしょうか?
public class Example02
{
public static void Main(string[] args)
{
Main(new string[] { "Hello" });
}
}
メインメソッドの中で自分自身のメインメソッドを呼び出しています。呼ばれたメインメソッドの中でもさらに同じメインメソッドを呼び出すので、無限に呼び出しが続きます。これを再帰処理といいますが、今回は終了条件を設けていないため、結果的にエラーを起こして終了します。
実行すると以下のようなエラーが発生するでしょう。
Unhandled exception. System.StackOverflowException: Exception of type 'System.StackOverflowException' was thrown.
at Example02.Main(String[] args)
...
StackOverflowException
ですね。メソッドの呼び出し履歴を保持するスタック領域があふれた(オーバーフロー)ということです。
しかし、PC本体が物理的に壊れるわけではありません。C#のプログラムが原因でパソコン自体が故障することは基本的にありません。安心してどんどんエラーを出してください。
エラーはできるだけ早く起こるほうがいいと思いませんか? 後になって深刻化するより、早期に異常を検知してくれるほうが被害が少なくて済みます。C#もJavaと同様に、早め早めにエラーを通知してくれる設計になっています。エラーは大歓迎すべき存在とも言えます。
8.プログラムはメモリ上にロードされて実行される
先ほどのスタックオーバーフローの例で分かってほしいのは、メソッドはメモリのスタック領域に呼び出し順に積み上げられるということです。
Main → Main → Main → Main → …(無限に呼び出し)
のようにスタックが埋まってしまい、あふれてエラーになります。
ソースコードそのものが動いているのではありません。
ソースコードをコンパイルして得られた実行コードがメモリにロードされ、CPUによって実行されています。
public
はアクセス修飾子。static
は静的メソッドの意味。void
は戻り値がないという意味。
このような修飾子やメモリへのロードを理解すると、オブジェクト指向の「インスタンスを作る」「動的に生成する」といった考え方も徐々に分かりやすくなります。
CPUとメモリの関係
プログラミングにおいて、CPU(中央処理装置)とメモリ(主記憶装置)はそれぞれ異なる役割を果たします。
- CPU … コンピュータの「脳」。プログラムの命令を解釈し、計算処理を行う。
- メモリ … プログラムコードや実行中のデータを一時的に格納する場所。CPUは必要なデータをメモリから読み込み、命令を実行する。
9.システムに必要なIPO
「システム」とは仕組みのことです。システムには必ず、Input → Process → Output という流れ(IPO)が存在します。
人間も1つのシステムと見れば、目や耳などで入力し(Input)、脳で思考・判断し(Process)、口や手足でアウトプット(Output)していますよね。C#プログラムも同じです。システムをさらに分割するとサブシステムになり、さらに小さく分解するとメソッド単位になります。
中学校時代に学んだ文字式、たとえば
y = 2x + 1
のように、xの値が変わればyの値も変わる。これが「引数」と「戻り値」に相当すると考えるとイメージしやすいでしょう。
10.コメントの入れ方
コメントは「人間に向けたメッセージ」です。他人、そして将来の自分に向けてのメモとして非常に重要です。
/**
* 画面表示
* 作成者 yamazaki
* 作成日 20XX/11/09
*/
public class Example03
{
public static void Main(string[] args)
{
// Hello Worldと画面に表示する
Console.WriteLine("Hello World");
}
}
C#のコメントには主に2つの方法があります。
/*
と*/
で囲んで複数行をコメントにする。//
を先頭につけて、その行をコメントにする。
さらに、///
で始まるコメントはXMLドキュメントコメントという形式になり、ツールを使って自動的にドキュメントを生成できます。JavaでのJavadocに近いイメージです。
public class Example04
{
public static void Main(string[] args)
{
Console.WriteLine("Hello World");
// Console.WriteLine("Goodbye World");
}
}
上記のようにコメントアウトすると、その部分は実行されなくなります。IDEにはコメント用のショートカットがあるので活用しましょう。
なお、納品するプログラムにはコメントアウトしたコードを残さないようにするのが一般的です。本書では冗長を避けるためコメントは最小限にしていますが、ソースコード学習用に重要箇所へコメントを入れるのは大いにおすすめです。
11.エラーへの対処
コンパイルするときにエラーが表示されることがあります。
たとえば、文末のセミコロン;
を忘れると
Example04.cs(4,40): error CS1002: ; が必要です
のようなエラーが表示されます。(実際のメッセージはIDEやコンパイラのバージョンによります)
このエラーメッセージをよく読まない人がいますが、エラーメッセージには非常に重要な情報が含まれています。
- Example04.cs(4,40) … Example04.csの4行目・列40付近に問題があることを示す。
エラーが出たらまずはメッセージをじっくり読み、それでも原因が分からなければ検索してみましょう。
C#のエラーには主に以下の3種類があります。
- 文法エラー (Syntax error) … コンパイル時に発見される
- 実行時エラー (Runtime error) … コンパイルは通るが、実行中にエラーが発生してプログラムが異常終了する
- 論理エラー (Logic error) … 実行はできるが、結果が意図と違う
当社の新人エンジニア研修ではIDEを使用するため、1の文法エラーはIDE上で即座に指摘してくれます。ただし、全角スペースなどメッセージが分かりづらいエラーもあるので注意しましょう。
2の実行時エラーは「例外処理」で対応します。
3の論理エラーは「テスト」や「デバッガ」で原因を追及します。
タイミングを見計らってデバッガの使い方も講師から説明があります。
12.標準出力
「Hello World」の文字列が表示された画面を「コンソール」といいます。C#の開発環境(IDE)には「Outputウィンドウ」「Debugコンソール」などの名称で用意されていることもありますが、いずれも「標準出力」と呼ばれるしくみを利用しています。
- 標準出力 … プログラムが標準的に利用する出力先。コンソールやIDEの出力ウィンドウなど。
C#で標準出力に出力したあと改行するには、
Console.WriteLine(出力内容);
と書きます。
改行を入れずに出力したい場合は、
Console.Write(出力内容);
です。
実行結果を目で確認できると、バグの修正(デバッグ)や動作確認がしやすくなります。目に見えない内部処理だけだと原因追及が難しいためです。
標準出力以外にも、ウェブページ(ASP.NETなど)に表示したりファイル出力したりする方法もあります。研修後半で扱う予定ですのでお楽しみに。
以上、C#の最初の一歩となる説明でした。Java同様に、
- 大文字と小文字は区別される
{
と}
は必ず1対1で対応させる{ }
で囲まれた範囲を「ブロック」と呼ぶ
といった基本ルールを守ることが重要です。また、コードの可読性を上げるため、インデント(字下げ)を適切に行いましょう。IDEのフォーマット機能を活用するのもおすすめです。
新入社員の方が呼び方に迷いそうな各種記号や、注意点などを章末にまとめていますので参考にしてください。
今後もオブジェクト指向の概念やクラス設計、デバッグやテストなど、さらに深い内容を研修で扱っていきます。C#をじっくり習得していきましょう。