この記事は、以前Mediumに投稿した記事
VTuberと一緒に学ぶC#プログラミング入門【#0】
~Hour of Codeが気付かせてくれるプログラミングの原理
https://link.medium.com/KjYrrl6ZpX
の続きです。
※本記事は筆者(VTuberの技術を理解するためUnityを学んでいる文系のプログラミング初心者です。プログラミングつよつよのVTuber、というわけではありません。そっちを期待した方はごめんなさい…。)の学習の記録になっています。想定している読者は、Hour of Codeをやってみた程度のまったくのプログラミング入門者です。プログラミングの経験が豊富にある方で、記事の内容に間違いを発見された読者の方がいらっしゃいましたら、遠慮なくご指摘いただけますと助かります。
#はじめに ~課題の細分化~
自分にとって困難な課題に取り組むときは、それをなるべく細かい要素に分けましょう。口に入らない食べ物を細かく切るように、段差を車いすで上がるときにスロープを用いるように、自分が「これならできる」と思えるまで、どこまでも小さい単位に課題を細分化し、その最小単位を少しずつ処理することによって、自分には到底できないと思い込んでいたことが、思いのほか、だんだんとできるようになってきます。大切なのは__「できない(のでやらない)」という選択肢を頭から取り除く__ことです。Unityで動くC#のプログラムを自作できるようになることはとても高い目標ですが、少しずつ上達していける小さい単位の課題を見つけていくことにしましょう。
#あわてない。あわてない。
新しいプログラミング言語を学ぶ唯一の道は、
それでプログラムを書いてみることである。『プログラミング言語C』(第2版)Brian W. Kernighan、Dennis M. Ritchie
C#によるプログラムは、Unityがないと機能しないものではないので、今後ずっとUnityの機能を拡張するものを作るにしても、Unityのない最小の環境で、最小のコードを書くことでC#プログラムが動く様子を見てみる__ことは有益だと思います。ここでは、最も単純で最も有名なプログラムと一般的に言われている『Hello World』(画面に文字を表示するプログラム)が読めるようになる__、ということを今回の目標、プログラムを学ぶための最小単位の第1歩として取り組んでみます。
using System;
class Hello
{
static void Main()
{
Console.WriteLine("Hello World!");
}
}
Hour of Codeのブロックプログラミングによって、JavaScriptというプログラミング言語を小一時間かじった程度の知識でも、Console.WriteLine("Hello World!");
という1行が文字を表示する命令なのは何となくわかる気がするのですが、それ以外のブロック
(波かっこ{}によって入れ子状に囲まれた範囲)がいろいろあってそこに未知のコードが書いてあるせいで__「こんなに短いコードなのに読めない!!」__という気持ちになってしまいますね…(なりませんか?)。ともあれ順番に、どういう意味のあるコードなのか調べていきます。
##主な参考サイト
C# によるプログラミング入門
https://ufcpp.net/study/csharp/
こちらの入門サイトを主に読んでいきます。目次を見ていると未知の単語がずらっと並んでいて回れ右しそうになるのですが「ごく短いコードが読めるようになる」という目標にそって、若干順番を前後しながら進んでいくことにしました。
##プログラムの「実行」とそのための環境
わたしたちがHour of Codeで書いてきたJavaScriptと違って、C#はソースコード
(ただのテキストとしての状態のプログラム)をコンパイル
(コンピュータが解る形式に変換すること)してから実行する__コンパイラ型言語__です。以下のページからその大まかなイメージが読み取れます。(このあたりはUnityのためのプログラムを作る際はまた変わってくるので、おおまかな理解で先に進みます。)
https://ufcpp.net/study/csharp/st_compile.html
https://wa3.i-3-i.info/word189.html
##ブラウザでコードを書いて実行できるお手軽環境
Wandbox
https://wandbox.org/
C#のプログラムを書いて実行する環境には、Visual Studioのような本格的なものをはじめ様々なものがあるのですが、本格的な開発環境になればなるほど、ごく短いテキストをコンパイルして実行する、というごく単純なことをするためには装備が大げさすぎて、動作も重く、そのせいで何となくやる気が奪われがちな気がします。そこで、今回はもっと手っ取り早く、__ブラウザだけ__でコードを書いて実行できるWandboxを用いて学んでいくことにします。
記述する言語としてC#を、一番上のmcsコンパイラを選択し、コンパイラ選択ボタンの脇にあるLoad template
をクリックすると、コードを記述するための領域に以下のようなWandbox標準のHelloWorldコードが自動で記述されます。
// This file is a "Hello, world!" in C# language by Mono for wandbox.
using System;
namespace Wandbox
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, Wandbox!");
}
}
}
// Mono references:
// http://www.mono-project.com/
// C# language references:
// https://msdn.microsoft.com/library/618ayhy6.aspx
Run(実行)ボタンをクリックすると、画面に文字(Hello, Wandbox!)が表示されました。これが正しい実行結果です。
#Hello Worldコードの比較
以下は、C#をつくったマイクロソフト社の公式ドキュメント( https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/language-specification/introduction )にある、よりシンプルなHelloWorldのコードです。これをWandboxにコピペして実行すると、同じように「Hello, World」という文字が表示されます。
using System;
class Hello
{
static void Main() {
Console.WriteLine("Hello, World");
}
}
__ふたつのプログラムは同じ機能をもつもの__であることがわかりましたが、先に見たWandboxが自動で記述したコードのほうが、いろいろと長い記述になっています。二つのコードの、共通しない部分を調べていきます。
##改行、インデント(字下げ)
using System;class Hello{static void Main(){Console.WriteLine("Hello, World");}}
C#のプログラムでは、改行とインデント(字下げ)は__ソースコードを読みやすくする機能を持つだけ__で、プログラムそのものには影響しません。よって、すべて削除しても同じようにプログラムは機能します。
##コメント
// This file is a "Hello, world!" in C# language by Mono for wandbox.
// Mono references:
// http://www.mono-project.com/
// C# language references:
// https://msdn.microsoft.com/library/618ayhy6.aspx
コメント
https://ufcpp.net/study/csharp/st_comment.html
//
(スラッシュ2つ)の後に書いた文章をコメント
と言います。コードの中に、__プログラムの機能に影響を及ぼさないようにしてコードの説明を書く__ことができます。
##namespace(名前空間)
namespace Wandbox
{
(省略)
}
名前空間
https://ufcpp.net/study/csharp/sp_namespace.html
namespace
は、__あらかじめプログラムの中身を分類しておく仕組み__です。このブロックは、マイクロソフトのコードに存在しないことからも判るように、差し当たり__無くても機能します__ので、後から詳しく学ぶことにします。
##string[] args
static void Main(string[] args)
static void Main()
WandboxのコードのMain()
のカッコの中にのみあるstring[] args
の部分も、無くても同じように機能するので、あとで学ぶことにします。
using System;
class Program
{
static void Main()
{
Console.WriteLine("Hello, Wandbox!");
}
}
これで、Wandboxのコードがマイクロソフトのコードとほぼ同じになりました。以下は、マイクロソフトのHelloWorldコードのみに絞って調べていきます。
#コードの不備によるエラー
ここから先は、画面に文字を表示するという機能に影響するコードです。__削ってしまうとエラー__が起こります。
Hello Worldプログラムの1行目using System;
を削除すると上記のように赤字でエラーが表示されます。それ以外の諸々の箇所も、それを削ったことによって諸々のエラーとなります。また、System
Main
などの予約語
(C#プログラムの中で決まった意味を持つ文字)は__大文字小文字を区別します。__大文字で書いてあるところを小文字で書いてしまうと、これもやはりエラーになります。
ということで、私が学ばないといけない最低限のコードはこの8行の中身であることが判明しました。これならばなんとか読み取れそうな気がします。でも、これだけのコードでも、まったく未知の状態からだと、かなり多くの概念を学ばないと意味がわかりません。
#コードの構造と「オブジェクト指向」
while (notFinished()) {
if (isPathForward()) {
moveForward();
} else {
if (isPathRight()) {
turnRight();
} else {
turnLeft();
}
}
}
Hour of Codeで書いたJavaScriptは、命令
が条件
に挟まれたものでした。
using System;
class Hello
{
static void Main() {
Console.WriteLine("Hello, World");
}
}
しかし、C#のHello Worldのコードは、命令を成立させるために、__まったく違う構造を取る仕様__になっています。このような書き方をする決まりを定めただけでなく、__このように書かないとプログラムが動かない__ような仕様にしてあるのは、C#がオブジェクト指向
であるから、らしいです。
オブジェクト指向とは何なのか…。__正直、詳しくはよくわからない__です。
##オブジェクト指向とは ~深淵を覗いてみる。
新人プログラマに知っておいてもらいたい
人類がオブジェクト指向を手に入れるまでの軌跡
https://qiita.com/hirokidaichi/items/591ad96ab12938878fe1
オブジェクト指向とは何なのか、ということを一生懸命調べていくと、たいへんに奥深い深淵を覗き込むことになります。上記の記事を読んで「ああなるほど」と思えるように、いつかはなりたいものだなあと思いながら、もう少しシンプルな理解が得られる情報に戻ります。
オブジェクト指向とは
https://ufcpp.net/study/csharp/oo_about.html
オブジェクト指向は、プログラムが大きく複雑になってきたときに諸々の非効率が起こらないように、構造が明確な書き方をする、という考え方に則って確立してきたものなのですね。
ではつづいて、一行ずつ、さらに細かく見ていくことにします。
#using System;
using System;
英語を直訳すると、__システムを使用している。__となり、なんとなく機能がわかるような気がしなくもないですが、未知の語と未知の語の組み合わせですので、知ったかぶりしないで調べます。
##using
//using System;を削除
class Hello{
static void Main(){
System.Console.Write("Hello World!"); //System.を追加
}
}
using System;
と書くかわりに、Console.Write("Hello World!");
の前にSystem.
を付け加えると、同じ実行結果になります。逆に言うと、using
によって命令のあるブロックの外に置くことで、__命令が増えたときに命令ごとにいちいちSystem.
と書かないで済むように省略している__わけです。これをusingディレクティブ
と言います。
##System
System 名前空間(まだ詳しく読まなくて大丈夫です。)
https://docs.microsoft.com/ja-jp/dotnet/api/system?view=netframework-4.8
C#をはじめとした大規模なプログラム構築に対応した言語は、どのようなプログラムでも使う基本的な機能を効率的に実装するために、ライブラリ
__(あらかじめ機能単位に分類されて用意してあるいろいろなパーツ)をプログラムの実行時に含めて使う__ことができるようになっています。(Hello Worldのプログラムでいえば、出力画面に文字を表示する、という機能の具体的な詳細に相当する部分です。)そのおかげで、同じ機能を実現するために、プログラム毎に同じコードをいちいち書く手間が省けます。今後、プログラムを実際に作っていく中では、ほぼ必ず何かしら、このような出来合いのライブラリを用いていくことになります。
##;(セミコロン)
文
https://ufcpp.net/study/csharp/st_variable.html#statement
;
(セミコロン)は文
(プログラムの意味ある単位)を区切るためのものです。人間は改行などによって文の区切りを見分けることができますが、先に書いたようにコンピュータは改行を無視するようになっており、__セミコロンがないと文の区切りを判断できない__ため、コンパイルの際エラーになります。
#class Hello{}
たぶん、C#のようなオブジェクト指向のプログラム言語を学ぼうとして、初心者にとっていまひとつよく理解できなくて困り果てる概念のひとつがクラス
ではないでしょうか。(わたしはそうです。)
以前、わたしがC#以外の言語を学んでいたころ(何の言語か、思い出せない…。)プログラムに詳しい友人に__「クラスってなに?」と質問したところ、帰ってきた答えは「学校のクラスみたいなものだよ」__という答えでした。実際、クラス
という言葉を日常語として思い浮かべた場合、プログラミングの経験のない人が思い浮かべるのは学校のクラスのイメージですよね。
学校のクラスは__集合体__です。__あるもの(いるひと)として生徒、先生、教室、机、文房具などが含まれ、すること__として授業、レクリエーション、などが含まれた概念です。プログラム言語におけるクラスも、同じようにメンバ変数
(あるもの)とメソッド
(すること)を含む集合として定義されます。
プラトン編-イデア論とクラス/インスタンス
https://www.itmedia.co.jp/im/articles/0506/11/news011.html
クラス
https://ufcpp.net/study/csharp/oo_class.html
一方、いつもの入門サイトをはじめ、多くの解説では、クラスとは「オブジェクトの設計図」とか「オブジェクトの鋳型」などと呼ばれています。クラスを元にして作られた実体がインスタンス
である。つまり、インスタンスを生成する元になるものがクラスである、というように説明されています。
ただ、メンバ変数
やインスタンス
はこのHello Worldのコードには出てこないので、ここではまだ説明できません。これらは後で学ぶことにしますが、学校のクラスの概念に当てはめてみると、__「教室にあるもの(いるひと)」と「教室ですること」を定めて集めたもの__がクラスで、実際の__4年3組__がインスタンス、というような感じになるでしょうか。(現実には学校のクラスという言葉はもうすこし異なる文脈で使われますが。)
Hello Worldのコードでは、Helloクラス
のブロックのなかに、すること(処理)が含まれることになります。クラスの中に定義された処理をメソッド
と呼びます。Helloは任意に定められたクラスの名前ですが、マイクロソフトのコードのように、そのクラスのことがわかりやすい名前にするのが良いようです。
##なぜいきなり命令を書いてはいけないのか
using System;
static void Main() {
Console.WriteLine("Hello, World");
}
ダメです。
Hello Worldのコードからこの__Helloクラスを削除するとプログラムが動作しません。__別にクラスに含めなくても、文字を表示する実際の処理だけ記述すれば問題ないような気もしますが、C#のプログラムでは__仕様として__そういう書き方はできないことになっています。
C# にはグローバル変数やグローバル メソッドはありません (言語によっては、グローバル変数やグローバル メソッドが存在する場合もあります)。 プログラムのエントリ ポイントである Main メソッドでも、クラスまたは構造体で宣言する必要があります。
https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/classes-and-structs/
クラスに含めずに本文に直接定義する処理をグローバルメソッド
と呼びますが、C#のプログラムにそれを作ろうとするとコンパイルの際エラーになります。しかし、メソッドがクラスの中にしか記述出来ないというルールは、あらゆるプログラミング言語で共通して決められているものではありません。
Say ‘Hello World’ in 28 Different Programming Languages
https://excelwithbusiness.com/blog/say-hello-world-in-28-different-programming-languages/
このサイトは様々な言語でHello Worldを記述して集めたものですが、メソッドがクラスの中にしか記述出来ないのはJavaやC#、その派生言語くらいで、あまり多くないことがわかります。
#include <stdio.h>
int main(void)
{
printf("hello, world\n");
}
ちなみに、C言語のHello Worldもこのようなもので、クラスを定義しなくても動作します。歴史的には、比較的新しい言語でグローバルメソッドが仕様として定義できなくしてあるのは、古い言語でそれを用いたプログラムによっていままでいろいろな問題が起こってきたからのようです。Hello Worldのような極めて短くて機能の限定されたコードでは面倒な記述が増えるだけのように思えますが、これも__後々プログラムが大きく複雑になってきたときのためにあるもの__として考える必要があるようです。
#static void Main(){}
static void Main() {
(Mainメソッドの処理内容)
}
Mainメソッド
(することの主なもの)を定義します。Hello Worldでは文字を表示する命令がひとつ含まれるだけなので実感が湧かないと思いますが、Mainメソッドは、C#のプログラムにとって特別な意味があるメソッドです。static
と void
はMainメソッドの性質を示すもので修飾子
(しゅうしょくし)と呼びます。Mainメソッドにおいては、static
は他のものに変更できませんが、void
は別の選択肢があります。
##Main()
Main
はメソッドの名前ですが、クラス名Hello
と違って自由に名づけられる名前ではなく、変更して実行しようとするとエラーになります。Mainメソッドは__ここからすべての処理を開始するよう、あらかじめC#の仕様として定められている__必須のメソッドです。これをエントリーポイント
と言います。
エントリーポイント
https://ufcpp.net/study/csharp/structured/miscentrypoint/
()
(括弧)の中には引数
(ひきすう:メソッドに渡して処理を行う値)を入れますが、何も書かなくてもHello Worldプログラムは機能するので後で学びます。ただし、引数が入らないからといって__括弧も外してしまうとエラー__になりますので、引数がない場合は、中に何も入っていない括弧だけ書くことになります。
##メソッド(すること)
プログラム内ですることにあらかじめ名前をつけてまとめておくのは、プログラムの別の場所で同じことをするとき__繰り返し同じコードを書かなくて済む__ようにするためです。学校のクラスに例えてみると、
【そうじをする】
・机といすを片付ける
・床をほうきで掃く
・床をぞうきんで拭く
・机といすを元の位置にもどす
別の場所では
普段は、5時間目が終わったら【そうじをする】
半日登校の日は、3時間目が終わったら【そうじをする】
大掃除のときは【そうじをする】ほか、窓ふきをする
と、(すること)の名前だけ書けば済むようにします。こうしておけば、「半日登校の日だけぞうきんがけを忘れた!」というような間違いが起こりにくくなります。
##static
Mainメソッドが静的メンバー
であることを示します。
静的メンバー(static member)とは、 特定のインスタンスにではなく、クラスに属するフィールドやメソッドのことです。
https://ufcpp.net/study/csharp/oo_static.html
さきほどちょっとお話しましたが、MainメソッドしかないHello Worldのプログラムではインスタンスを生成する過程が登場しないので、Mainメソッドが__インスタンスにではなくクラスに属している__、ということがどういう意味なのか具体的にイメージがわきません。これはインスタンスの生成について詳しく学ぶ際にあわせて学ぶことにします。しかし、
Main は static である必要があります
https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/main-and-command-args/
Mainメソッドはstatic
でないといけない仕様だそうなので、static以外に書き換えたりstaticを削除したりするとエラーになります。
##void
戻り値
(もどりち:メソッドから返ってくる処理の結果の値)の型
(かた:データの種類)を定義します。
戻り値
https://wa3.i-3-i.info/word1441.html
WandboxでHelloWorldを実行すると、実行結果として表示される「Hello World!」の文字列の次に、ピンクの文字で0
という値が表示されますが、これがMainメソッドの戻り値です。プログラムが正常に実行されると0
、エラーの場合1
が表示されます。緑色の文字のStart
とFinish
もそうですが、Wandboxが気を利かせて表示してくれているだけで、実際のコンソールアプリケーションでC#を実行した場合には表示されません。void
は__戻り値を持たない__メソッドであることを示しますので、Wandboxで0
と表示されるのは特別な仕様です。
Mainメソッドの戻り値にはこのvoid型と、整数
(英:integer)の戻り値を返すint型
の2種類が定義できます。
int型
https://wa3.i-3-i.info/word14966.html
using System;
class Hello{
static int Main(){
Console.Write("Hello World!");
return 3; //戻り値を返す命令
}
}
たとえば、このように記述すると、戻り値3
が実行画面に表示されます。いずれにしても、画面に文字を表示する機能しか持たないHelloWorldのプログラムではMainメソッドの戻り値は特に用いられないので、これも後で詳しく学ぶことにします。
#Console.Write("Hello World!");
こうしてわたしたちは長い長い遠回りを経てようやっと、中心になる命令文の部分にたどりつきました!
using System;の解説で触れましたが、この行はSystem.Console.Write("Hello World!");
のSystem.
をusing System;
によって省略したものです。つまり、System
名前空間にあらかじめ用意してあるConsole
クラスのWrite
メソッドを用いて、Hello World!
と表示する、ということです。
##Console
System名前空間のConsoleクラス
https://docs.microsoft.com/ja-jp/dotnet/api/system.console?view=netframework-4.8
コンソール
(プログラムの実行結果が表示される黒背景の画面)を扱うための入力
(キーボードで画面に文字を打つ操作など)や出力
(今回のHello Worldのようにプログラムの実行結果を表示する操作など)、エラー
(プログラムの間違いをコンソールに表示する操作など)に関する出来合いのコードがまとめられているクラスです。
##Write()
ConsoleクラスのWriteメソッド
https://docs.microsoft.com/ja-jp/dotnet/api/system.console.write?view=netframework-4.8
コンソールに__改行なし__で実行結果を出力します。文末で改行する場合はWriteメソッド
の代わりにWriteLineメソッド
を用います。
using System;
class Hello{
static void Main(){
Console.Write("指定した書式情報を使用して、指定したオブジェクトのテキスト表現と可変長パラメーター リストを標準出力ストリームに書き込みます。");
Console.WriteLine("指定したデータを標準出力ストリームに書き込み、");
Console.WriteLine("続けて現在の行終端記号を書き込みます。");
}
}
上記のコードを実行すると以下のようになります。一行目が画面端で折り返しされているのはWandboxの設定によるもので、プログラム側の改行によるものではありません。
##文字列
コンピュータが理解するための命令ではなく、人間が読むために入出力する文字の集まったデータを文字列
(もじれつ)と呼びます。文字列は、命令文などと区別するために"
(ダブルクオーテーション)で挟みます。
Console.Write("コンピュータはダブルクオーテーションで挟んだ部分を文字列として認識します。");
Console.Write(ダブルクオーテーションで挟まないとコンピュータが正しく認識できず、エラーになります。);
上記のQiitaフォーマットのコード表記では文字列は自動で青字で表示されるようになっています。2行目のように書くとエラー(赤い点線のアンダーラインによって、文法が間違いであることが示されています。)
#おわりに
__以上で、C#のHello Worldプログラムをすべて読み終わりました!まだ十分に把握できたとはいえない部分が残ってしまっていますが、最初の「まったく意味がわからない」という状態からは抜けることができたのではないでしょうか。まだまだ道のりは長く険しいですが、まったく読めなかったころと比べれば確実に進歩していると思いますので、自信を持ってもらえたらうれしいです。『VTuberと一緒に学ぶ』と言いつつ今回はVTuberのVの字も出てこない記事に__なってしまいましたが、次回は、いよいよ実際にUnityを用いたプログラミングを学んでいこうと思います!(筆者は年間数日ほどある極めてまれな「暇なとき」を用いてこの記事を書いているので、次もまた1年先とかかもしれませんが、今後もなるべく古くなりにくい初歩的なトピックを選んで書いていこうと思います。)