「あれ、変数が見つかりません」その原因、実はクラスの壁かもしれません

広告

C#の学習が進んでコンソールアプリからWindowsフォームアプリに移ったとき、多くの人が同じところでつまずきます。

「さっきまで動いていた変数が、急に『存在しません』って怒られる…」

今日はこの現象を、コンソールアプリの小さな例から順番に見ていって、「なぜダメなのか」を理屈で納得できるようにしていきます。

まずはコンソールアプリで再現してみる

こんなコードがあったとします。


using System;

namespace StudentSample
{
    internal class Program
    {
        static void Main(string[] args)
        {
            new Form1();
        }
    }

    class Form1
    {
        public string helloWorld;

        public Form1()
        {
            Console.WriteLine(helloWorld);

            Student student1 = new Student();
            student1.Name = "山田";
            student1.ShowScore();
        }
    }

    class Student
    {
        public string Name;
        public int Score;
        public void ShowScore()
        {
            Console.WriteLine(helloWorld);
        }
    }
}

あえてForm1という名前のクラスを使っています(この意図は後ほど回収します)。Form1クラスの中にhelloWorldという変数(正確には「フィールド」と呼びます)を用意し、コンストラクターの中でStudentを作ってShowScore()を呼び出しています。

このコードをビルドすると、Studentクラス側のhelloWorldのところにエラーが出ます。


エラー CS0103: 現在のコンテキストに 'helloWorld' という名前は存在しません

Form1のコンストラクターの中ではConsole.WriteLine(helloWorld);が問題なく通っているのに、Studentクラスの中に書いたまったく同じ一行がエラーになる。「同じnamespace StudentSampleの中に書いてあるのに、どうして見つからないの?」と思いますよね。ここが今日のポイントです。

クラスは「部屋」、フィールドは「部屋の中の私物」

変数(フィールド)を理解するときによく使う例えですが、クラスを「部屋」だと考えてみてください。

  • Form1クラス=Aさんの部屋
  • Studentクラス=Bさんの部屋
  • helloWorldという変数=Aさんの部屋に置いてある私物

Form1クラスの中に書いたhelloWorldは、あくまでForm1という部屋の中にある私物です。Studentという別の部屋にいる人が、ただ「helloWorld」と名前を呼んだだけでは、その私物を勝手に持ってくることはできません。名前を知っているかどうかと、実際に使えるかどうかは別問題なのです。

これは「同じnamespace StudentSampleの中に書いてあるから使えるはず」という感覚とはズレるところなので、最初は違和感があって当然です。namespaceはあくまで「同じ建物(アプリ)」くらいの単位で、部屋(クラス)はその中でさらに分かれている、とイメージすると整理しやすくなります。

では、どうすれば使えるのか

理由がわかったところで、直し方を考えてみましょう。ここで大事なのは、「Studentが本当に必要としているものは何か」を考えることです。

StudentShowScore()がやりたいことは、突き詰めると「もらった文字列を表示する」だけです。Form1の部屋そのもの(ラベルもボタンも含めた全部)が欲しいわけではありません。それなら、部屋ごと渡すのではなく、必要な私物(文字列)だけを渡してあげる方がシンプルです。

呼び出す側(Form1のコンストラクター)はこうなります。


class Student
{
    public string Name;
    public int Score;

    public void ShowScore(string message)
    {
        Console.WriteLine(message); // 渡された文字列を表示するだけでよい
    }
}

public Form1()
{
    Console.WriteLine(helloWorld);

    Student student1 = new Student();
    student1.Name = "山田";
    student1.ShowScore(helloWorld); // 自分の部屋にある私物を、そのまま渡す
}

Form1の中ではhelloWorldは自分の部屋の私物なのでそのまま使えます。それをShowScore(helloWorld)という形で値として渡すことで、Studentは「Form1という部屋がある」ということすら知らなくても仕事ができるようになります。

これは、Form1のインスタンスごと渡すやり方(ShowScore(Form1 form))と比べると、次のような違いがあります。

渡し方Studentが知る必要があること特徴
Form1のインスタンスを渡すForm1というクラスの存在、その中にhelloWorldがあることForm1の中の他のものにも触れてしまう(触りすぎる可能性がある)
文字列だけを渡す何も知らなくてよい必要な情報だけを受け取るので影響範囲が小さい
訪問数 4 回, 今日の訪問数 4回

広告

C#

Posted by hidepon