Edited at

VTuberと一緒に学ぶC#プログラミング入門【#1】~Hello Worldを読んでみたよ

この記事は、以前Mediumに投稿した記事

VTuberと一緒に学ぶC#プログラミング入門【#0】

~Hour of Codeが気付かせてくれるプログラミングの原理

https://link.medium.com/KjYrrl6ZpX

の続きです。

※本記事は筆者(VTuberの技術を理解するためUnityを学んでいる文系のプログラミング初心者です。プログラミングつよつよのVTuber、というわけではありません。そっちを期待した方はごめんなさい…。)の学習の記録になっています。想定している読者は、Hour of Codeをやってみた程度のまったくのプログラミング入門者です。プログラミングの経験が豊富にある方で、記事の内容に間違いを発見された読者の方がいらっしゃいましたら、遠慮なくご指摘いただけますと助かります。


はじめに ~課題の細分化~

slope.png

自分にとって困難な課題に取り組むときは、それをなるべく細かい要素に分けましょう。口に入らない食べ物を細かく切るように、段差を車いすで上がるときにスロープを用いるように、自分が「これならできる」と思えるまで、どこまでも小さい単位に課題を細分化し、その最小単位を少しずつ処理することによって、自分には到底できないと思い込んでいたことが、思いのほか、だんだんとできるようになってきます。大切なのは「できない(のでやらない)」という選択肢を頭から取り除くことです。Unityで動くC#のプログラムを自作できるようになることはとても高い目標ですが、少しずつ上達していける小さい単位の課題を見つけていくことにしましょう。


あわてない。あわてない。


新しいプログラミング言語を学ぶ唯一の道は、

それでプログラムを書いてみることである。

『プログラミング言語C』(第2版)Brian W. Kernighan、Dennis M. Ritchie


C#によるプログラムは、Unityがないと機能しないものではないので、今後ずっとUnityの機能を拡張するものを作るにしても、Unityのない最小の環境で、最小のコードを書くことでC#プログラムが動く様子を見てみることは有益だと思います。ここでは、最も単純で最も有名なプログラムと一般的に言われている『Hello World』(画面に文字を表示するプログラム)が読めるようになる、ということを今回の目標、プログラムを学ぶための最小単位の第1歩として取り組んでみます。


C#による『HelloWorld』のコード


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を用いて学んでいくことにします。

スクリーンショット-2019-06-.png

記述する言語としてC#を、一番上のmcsコンパイラを選択し、コンパイラ選択ボタンの脇にあるLoad templateをクリックすると、コードを記述するための領域に以下のようなWandbox標準のHelloWorldコードが自動で記述されます。


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


SS2.png

Run(実行)ボタンをクリックすると、画面に文字(Hello, Wandbox!)が表示されました。これが正しい実行結果です。


Hello Worldコードの比較

以下は、C#をつくったマイクロソフト社の公式ドキュメント( https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/language-specification/introduction )にある、よりシンプルなHelloWorldのコードです。これをWandboxにコピペして実行すると、同じように「Hello, World」という文字が表示されます。


Microsoft公式のドキュメントにあるHelloWorld

using System;

class Hello
{
static void Main() {
Console.WriteLine("Hello, World");
}
}


ふたつのプログラムは同じ機能をもつものであることがわかりましたが、先に見たWandboxが自動で記述したコードのほうが、いろいろと長い記述になっています。二つのコードの、共通しない部分を調べていきます。


改行、インデント(字下げ)


Microsoft公式のドキュメントにあるHelloWorld(改行、インデントなし)

using System;class Hello{static void Main(){Console.WriteLine("Hello, World");}}


C#のプログラムでは、改行とインデント(字下げ)はソースコードを読みやすくする機能を持つだけで、プログラムそのものには影響しません。よって、すべて削除しても同じようにプログラムは機能します。


コメント


WandboxテンプレートのHelloWorld(部分)

// This file is a "Hello, world!" in C# language by Mono for wandbox.



WandboxテンプレートのHelloWorld(部分)

// 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(名前空間)


WandboxテンプレートのHelloWorld(部分)

namespace Wandbox

{
(省略)
}

名前空間

https://ufcpp.net/study/csharp/sp_namespace.html

namespaceは、あらかじめプログラムの中身を分類しておく仕組みです。このブロックは、マイクロソフトのコードに存在しないことからも判るように、差し当たり無くても機能しますので、後から詳しく学ぶことにします。


string[] args


WandboxテンプレートのHelloWorld(部分)

static void Main(string[] args)



Microsoft公式のドキュメントにあるHelloWorld(部分)

static void Main() 


WandboxのコードのMain()のカッコの中にのみあるstring[] argsの部分も、無くても同じように機能するので、あとで学ぶことにします。


WandboxテンプレートのHelloWorld(短縮したもの)

using System;

class Program
{
static void Main()
{
Console.WriteLine("Hello, Wandbox!");
}
}


これで、Wandboxのコードがマイクロソフトのコードとほぼ同じになりました。以下は、マイクロソフトのHelloWorldコードのみに絞って調べていきます。


コードの不備によるエラー

ここから先は、画面に文字を表示するという機能に影響するコードです。削ってしまうとエラーが起こります。

SS3.png

Hello Worldプログラムの1行目using System;を削除すると上記のように赤字でエラーが表示されます。それ以外の諸々の箇所も、それを削ったことによって諸々のエラーとなります。また、System Mainなどの予約語(C#プログラムの中で決まった意味を持つ文字)は大文字小文字を区別します。大文字で書いてあるところを小文字で書いてしまうと、これもやはりエラーになります。

ということで、私が学ばないといけない最低限のコードはこの8行の中身であることが判明しました。これならばなんとか読み取れそうな気がします。でも、これだけのコードでも、まったく未知の状態からだと、かなり多くの概念を学ばないと意味がわかりません。


コードの構造と「オブジェクト指向」


Hour of Codeで最後に作ったJavaScriptのコード

while (notFinished()) {

if (isPathForward()) {
moveForward();
} else {
if (isPathRight()) {
turnRight();
} else {
turnLeft();
}
}
}

Hour of Codeで書いたJavaScriptは、命令条件に挟まれたものでした。


Microsoft公式のドキュメントにあるHelloWorld

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;


Microsoft公式のドキュメントにあるHelloWorld(部分)

using System;


英語を直訳すると、システムを使用している。となり、なんとなく機能がわかるような気がしなくもないですが、未知の語と未知の語の組み合わせですので、知ったかぶりしないで調べます。


using


usingを使わないHelloWorld

//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

library.png

C#をはじめとした大規模なプログラム構築に対応した言語は、どのようなプログラムでも使う基本的な機能を効率的に実装するために、ライブラリ(あらかじめ機能単位に分類されて用意してあるいろいろなパーツ)をプログラムの実行時に含めて使うことができるようになっています。(Hello Worldのプログラムでいえば、出力画面に文字を表示する、という機能の具体的な詳細に相当する部分です。)そのおかげで、同じ機能を実現するために、プログラム毎に同じコードをいちいち書く手間が省けます。今後、プログラムを実際に作っていく中では、ほぼ必ず何かしら、このような出来合いのライブラリを用いていくことになります。


;(セミコロン)



https://ufcpp.net/study/csharp/st_variable.html#statement

;(セミコロン)は(プログラムの意味ある単位)を区切るためのものです。人間は改行などによって文の区切りを見分けることができますが、先に書いたようにコンピュータは改行を無視するようになっており、セミコロンがないと文の区切りを判断できないため、コンパイルの際エラーになります。


class Hello{}

たぶん、C#のようなオブジェクト指向のプログラム言語を学ぼうとして、初心者にとっていまひとつよく理解できなくて困り果てる概念のひとつがクラスではないでしょうか。(わたしはそうです。)

class.png

以前、わたしがC#以外の言語を学んでいたころ(何の言語か、思い出せない…。)プログラムに詳しい友人に「クラスってなに?」と質問したところ、帰ってきた答えは「学校のクラスみたいなものだよ」という答えでした。実際、クラスという言葉を日常語として思い浮かべた場合、プログラミングの経験のない人が思い浮かべるのは学校のクラスのイメージですよね。

学校のクラスは集合体です。あるもの(いるひと)として生徒、先生、教室、机、文房具などが含まれ、することとして授業、レクリエーション、などが含まれた概念です。プログラム言語におけるクラスも、同じようにメンバ変数(あるもの)メソッド(すること)を含む集合として定義されます。

igata_model.png

プラトン編-イデア論とクラス/インスタンス

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#、その派生言語くらいで、あまり多くないことがわかります。


C言語によるHelloWorld

#include <stdio.h>


int main(void)
{
printf("hello, world\n");
}

ちなみに、C言語のHello Worldもこのようなもので、クラスを定義しなくても動作します。歴史的には、比較的新しい言語でグローバルメソッドが仕様として定義できなくしてあるのは、古い言語でそれを用いたプログラムによっていままでいろいろな問題が起こってきたからのようです。Hello Worldのような極めて短くて機能の限定されたコードでは面倒な記述が増えるだけのように思えますが、これも後々プログラムが大きく複雑になってきたときのためにあるものとして考える必要があるようです。


static void Main(){}


Microsoft公式のドキュメントにあるHelloWorld(部分)

static void Main() {

Mainメソッドの処理内容)
}

Mainメソッド(することの主なもの)を定義します。Hello Worldでは文字を表示する命令がひとつ含まれるだけなので実感が湧かないと思いますが、Mainメソッドは、C#のプログラムにとって特別な意味があるメソッドです。staticvoidは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

SS2-2.png

WandboxでHelloWorldを実行すると、実行結果として表示される「Hello World!」の文字列の次に、ピンクの文字で0という値が表示されますが、これがMainメソッドの戻り値です。プログラムが正常に実行されると0、エラーの場合1が表示されます。緑色の文字のStartFinishもそうですが、Wandboxが気を利かせて表示してくれているだけで、実際のコンソールアプリケーションでC#を実行した場合には表示されません。void戻り値を持たないメソッドであることを示しますので、Wandboxで0と表示されるのは特別な仕様です。

Mainメソッドの戻り値にはこのvoid型と、整数(英:integer)の戻り値を返すint型の2種類が定義できます。

int型

https://wa3.i-3-i.info/word14966.html


戻り値が整数(int型)のMainメソッド

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メソッドを用います。


WriteメソッドとWriteLineメソッド

using System;

class Hello{
static void Main(){
Console.Write("指定した書式情報を使用して、指定したオブジェクトのテキスト表現と可変長パラメーター リストを標準出力ストリームに書き込みます。");
Console.WriteLine("指定したデータを標準出力ストリームに書き込み、");
Console.WriteLine("続けて現在の行終端記号を書き込みます。");
}
}

上記のコードを実行すると以下のようになります。一行目が画面端で折り返しされているのはWandboxの設定によるもので、プログラム側の改行によるものではありません。

スクリーンショット-2019-10-.png


文字列

コンピュータが理解するための命令ではなく、人間が読むために入出力する文字の集まったデータを文字列(もじれつ)と呼びます。文字列は、命令文などと区別するために"(ダブルクオーテーション)で挟みます。


文字列であることを示すため「"」で挟む

Console.Write("コンピュータはダブルクオーテーションで挟んだ部分を文字列として認識します。");

Console.Write(ダブルクオーテーションで挟まないとコンピュータが正しく認識できず、エラーになります。);

上記のQiitaフォーマットのコード表記では文字列は自動で青字で表示されるようになっています。2行目のように書くとエラー(赤い点線のアンダーラインによって、文法が間違いであることが示されています。)


おわりに

以上で、C#のHello Worldプログラムをすべて読み終わりました!まだ十分に把握できたとはいえない部分が残ってしまっていますが、最初の「まったく意味がわからない」という状態からは抜けることができたのではないでしょうか。まだまだ道のりは長く険しいですが、まったく読めなかったころと比べれば確実に進歩していると思いますので、自信を持ってもらえたらうれしいです。『VTuberと一緒に学ぶ』と言いつつ今回はVTuberのVの字も出てこない記事になってしまいましたが、次回は、いよいよ実際にUnityを用いたプログラミングを学んでいこうと思います!(筆者は年間数日ほどある極めてまれな「暇なとき」を用いてこの記事を書いているので、次もまた1年先とかかもしれませんが、今後もなるべく古くなりにくい初歩的なトピックを選んで書いていこうと思います。)