コードの読み方(基礎編)

はじめに

プログラミング経験のない友人がUnityを始めたので、応援記事を書いていきます。

ソースコードは簡略なものにしています。 なお動作確認は .Net Fiddle を利用しています。

dotnetfiddle.net

ブログで目指すこと

全体を通して目指すこと

本ブログはごくごく親しい友人が「Unityを触りてぇ!」となり、途中で色々と苦心しているようなので、口頭で解説した内容を参照できるようにブログにおこしていくものになります。 多分UnityからC#に入る人も居るだろうし、助けになればいいなと。

筆者はC#を学習したことはあるものの業務で利用したことはないため、細かな作法は正しくない恐れがあります。 *1

なお、Unityの学習には次の本を使っているそうです。 僕も買おうかな。

gihyo.jp

今回のブログで目指すこと

色々と書いていたら長くなったので、次の1点に絞っていきます。

  • コードの基本的な読み方がわかる

処理順のイメージを作る

Main メソッドから始める

プログラムの動作をイメージする時、「どこからプログラムが実行されていくか」を把握していく必要があります。 今回は Program クラスの Main メソッドから始まるプログラムを書きます。

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

このコードを実行すると、お馴染みの Hello World がコンソールに出力されます。 めでたい。

docs.microsoft.com

Main メソッドは、C# アプリケーションのエントリ ポイントです (ライブラリおよびサービスでは、エントリ ポイントとしての Main メソッドは必要ありません)。アプリケーションを起動すると、最初に Main メソッドが呼び出されます。

C#のアプリケーションの場合 Main メソッドから処理が始まることになっています。 もしも Main メソッドが書いているコードにない場合には、そのコードを呼び出している箇所をたどっていくと Main にたどり着く、と理解してみてください。

ソースコードを上から下に読んでいく

Main メソッドの中を考えていきます。

次のように2行にわたってコードが書いてあるとき、上から順に実行されていきます。 順々に読んでいけばいいので、理解しやすくて助かります。

using System;
                    
public class Program
{
    public static void Main()
    {
        Console.WriteLine("No.1");
        Console.WriteLine("No.2");
    }
}

コンソールに表示されるのは、次のような文字列です。 ちゃんと上から下に実行されていることが確認できます。

No.1
No.2

では、次のように書かれている場合を試してみます。

using System;

public class Program
{
    private static void topMethod1()
    {
        Console.WriteLine("call topMethod1");
    }

    private static void topMethod2()
    {
        Console.WriteLine("call topMethod2");
    }

    public static void Main()
    {
        Console.WriteLine("start Main");

        topMethod1();
        bottomMethod();
        topMethod2();

        Console.WriteLine("end Main");
    }

    private static void bottomMethod()
    {
        Console.WriteLine("call bottomMethod");
    }
}

すると、次のような表示が出ます。

start Main
call topMethod1
call bottomMethod
call topMethod2
end Main

ごくごく当然のように思えますが、話の整理をしておきます。 このコードから確認できるのは次の3つの話です。

  1. Main メソッドは一番上に書いてなくても、最初に実行される
  2. ソースコードで書かれているファイルの順番ではなく、Main メソッドに書かれている順番に実行される
  3. ソースコード上の余白と、コンソールに出力される余白には関連がない

メソッドを作る

当たり前のように Main()bottomMethod() などを書いていますが、これらの「処理をひとまとめにしたもの」を「メソッド(method)」と呼びます。

docs.microsoft.com

メソッドは、一連のステートメントが含まれているコード ブロックです。 必要なメソッド引数を指定してプログラムからメソッドを呼び出すと、メソッド内のステートメントが実行されます。 C# では、実行されるすべての命令がメソッドのコンテキストで実行されます。

メソッドはプログラムを作る人の「意思」で作成できます。 個人的な理解ですが、プログラミングは メソッド の集合体を作る作業であって、その結果出来上がるものが クラス であり ライブラリソフトウェア と呼ばれるプログラムです。

つまり、下記のような事柄はプログラムを書く人(プログラマー)の自由です。

  1. どのような処理を「メソッド」にするか
  2. 処理をどのように「メソッド」にまとめるか
  3. 「メソッド」のインプットとアウトプットをどうするか

「価格を渡すと、消費税込みの金額にする」や「ユーザーがボタンをクリックすると、ダイアログを表示する」などなど、メソッドとして作成されるものは多岐にわたります。 「Yボタンを押すと、キャラクターが向いている方向で"しらべる"操作をする」や「文字入力欄を表示して、入力された なまえ をキャラデータとして保存する」ような、そういった処理もメソッドとして定義されていく、そんなイメージができるのではないでしょうか。 なお、先程は「呼び出すと"call bottomMethod"とコンソールに表示する」メソッドである bottomMethod を作成していました。

補足としては、 Main メソッドの1つだけ使う、ということもできます。 実際に、一番最初の例では Main のみ使っていました。

public static void Main()
{
    Console.WriteLine("No.1");
    Console.WriteLine("No.2");
}

一方で、2桁の行数を超えるメソッドを書いてくると、だんだん大変になってきます。 もしも Main メソッドを分割しないまま開発し、数万行を超えてしまったらどうなってしまうでしょうか? *2

メソッドの分割は非常に難しく、 正解 がわかりにくい作業です。 基本的には「不必要に大きなメソッドを作らない」ことが正解なのですが、どの程度が「不必要に大きいのか」はわかりにくいため、コードを友人や同僚に見せて議論してみることをお勧めします。

一方で コーディング規約 という「C#のコードならこうやって書きましょう」といった指標が示されています。 C#であればMicrosoftが提案するコーディング規約に従っておけば、C#を読み書きできる人にとって共通のルールとなるので、共有などがしやすくなっていきます。

docs.microsoft.com

戻り値

ファーストネームとラストネームを受け取って、フルネームにする」という処理を考えていきます。

String fullName(String firstName, String lastName) {
    return firstName + " " + lastName;
}

StringfirstNamelastName を受け取り、それを半角スペースを含めた結合をして返すメソッドができました。 このように返される値のことを「戻り値」と呼びます。

docs.microsoft.com

メソッドは、呼び出し元に値を返すことができます。 戻り値の型 (メソッド名の前に記述されている型) が voidでない場合、メソッドは、return キーワードを使用して値を返すことができます。 return キーワードに続いて変数、定数、または戻り値の型に一致する値が記述されたステートメントは、その値をメソッドの呼び出し元に返します。 戻り値の型が void 以外のメソッドで値を返すには、 return キーワードを使用する必要があります。 また、 return キーワードは、メソッドの実行を中止します。

これを実際に呼び出してみましょう。 firstNamelastName から、 name を作ってコンソールに表示してみます。

using System;

public class Program
{
    public static void Main()
    {
        String firstName = "Taro";
        String lastName = "Yamada";

        String name = fullName(firstName, lastName);
        Console.WriteLine(name);
    }

    private static String fullName(String firstName, String lastName) {
        return firstName + " " + lastName;
    }
}

これで String 型の戻り値をもつメソッドを作ることができました。 戻り値が不要な場合は Main のように void で宣言することになります。


続いて、メソッドを拡張してみましょう。 例えば「日本式に lastName + firstName で表示する」こともできるようにしていきます。

using System;

public class Program
{   
    public static void Main()
    {
        String firstName = "Taro";
        String lastName = "Yamada";

        String name = fullName(firstName, lastName);
        Console.WriteLine(name);
    }

    private static String fullName(String firstName, String lastName) {
        if (isInJapan()) {
            return lastName + " " + firstName;
        } else {
            return firstName + " " + lastName;
        }
    }

    private static bool isInJapan() {
        return true;
    }
}

今回は簡易的に isInJapan() というメソッドを作ってみました。 本来はSystemのロケール情報や、ユーザー設定から取得し切り替えるような情報です。 return true; と書いてる箇所を差し替えることになります。


処理をメソッドを使って切り分けていくイメージができたでしょうか?

今回はここまで。

*1:主な経験としてはJavaやKotlin、SwiftやDartになっています。GitHub

*2:実際、あるところにはありますが……