別クラスから label1.Text = Name; と書くとエラーになるのはなぜ?

広告

WinFormsでクラスを分けて書いていると、フォーム上には確かに label1 があるのに、別クラスから参照するとエラーになることがあります。

class Player
{
    public string Name { get; set; }

    public void ShowName()
    {
        label1.Text = Name; // ここでエラー
    }
}
Visual StudioのWinFormsデザイナーとコード画面。Playerクラスからlabel1を参照してエラーになっている例

なぜ使えないのでしょうか。ポイントは、コントロールもクラスのメンバーであり、それを持っているクラスの外から名前だけで参照することはできないという点です。この記事では、エラーの理由と2つの解決方法を初心者向けに整理します。

結論:label1はForm1のメンバーだから

フォーム上に配置したLabelコントロールは、Visual Studioのデザイナーによって Form1 クラスのフィールドとして作成されます。イメージは次のとおりです。

public partial class Form1 : Form
{
    // label1 は Form1 が持っている
}

一方、Player クラスが持っているのは Name プロパティです。

class Player
{
    public string Name { get; set; }
}

つまり、所有関係は次のようになっています。

Form1はlabel1を持ち、PlayerはNameを持つため、Playerからlabel1を直接参照できないことを示す図解
Form1とPlayerは、それぞれ別のメンバーを持っています。

ShowName()Player クラスの中にあります。そこで突然 label1 と書いても、C#から見ると「Playerの中にその名前のメンバーはありません」という状態です。画面に見えているかどうかではなく、今書いているコードがどのクラスに属しているかが重要です。

クラスが違えば、使える名前の範囲も違う

これはWinFormsだけの特別なルールではありません。たとえば、次の2クラスを考えてみましょう。

class Player
{
    public string Name { get; set; }
}

class Enemy
{
    public int Hp { get; set; }
}

Name はPlayerのメンバーなので、Enemyの中から単に Name と書いても参照できません。同じように、label1 はForm1のメンバーなので、Playerの中から単に label1 と書くことはできません。

方法1:どうしてもPlayer側で代入したいなら、Labelを引数で渡す

どうしてもPlayer側のメソッド内でLabelへ代入したい場合は、Form1からPlayerへLabelを引数として渡します。この方法なら、PlayerはForm1のlabel1を名前で直接参照せず、受け取ったLabelを操作できます。

class Player
{
    public string Name { get; set; }

    public void ShowName(Label label)
    {
        label.Text = Name;
    }
}

Form1側では次のように呼び出します。

private void button1_Click(object sender, EventArgs e)
{
    Player player = new Player();
    player.Name = "山田";

    player.ShowName(label1);
}

この場合、PlayerがForm1の label1 を名前で直接探しているわけではありません。Form1から渡されたLabelを、メソッドの引数 label として受け取って操作しています。

Form1
  │ label1を渡す
  ↓
Player.ShowName(label1)
  │
  ↓
label.Text = Name

学習用の小さなアプリや、特定のコントロールを操作することがメソッドの目的として明確な場合には、仕組みが分かりやすい方法です。

なぜ引数なら使えるのか

次のメソッドでは、label という名前の引数がメソッド内で定義されています。

public void ShowName(Label label)
{
    label.Text = Name;
}

そのため、メソッドの中で label.Text を利用できます。元の ShowName() には、label1 を受け取る仕組みがありませんでした。引数を追加することで、Playerが操作する対象を呼び出し元から明示的に受け取れるようになります。

方法2:UI更新をForm1側に寄せる

もう1つは、Playerにはデータを持たせ、Labelへの表示はForm1に担当させる方法です。

class Player
{
    public string Name { get; set; }

    public string GetName()
    {
        return Name;
    }
}

Form1側で値を受け取り、Labelへ設定します。

private void button1_Click(object sender, EventArgs e)
{
    Player player = new Player();
    player.Name = "山田";

    label1.Text = player.GetName();
}

なお、単に名前を取得するだけなら、プロパティを直接読んでも構いません。

label1.Text = player.Name;

この形では役割がはっきり分かれます。

  • Player:プレイヤーの名前など、データやゲーム上の処理を担当する
  • Form1:LabelやButtonなど、画面表示を担当する

PlayerがWinFormsのLabelを知らないため、あとからコンソールアプリや別の画面でPlayerを使いたくなったときにも再利用しやすくなります。

2つの方法はどう使い分ける?

どちらもエラーを解消できますが、目的が少し異なります。

方法向いている場面特徴
Labelを引数で渡すどうしても別クラス側で代入したいとき、小さな学習用アプリ動きが直感的。ただしPlayerがWinFormsに依存する
Form1側でUIを更新する役割を分けたいとき、今後機能を増やす予定があるときPlayerを再利用しやすく、変更箇所も整理しやすい

初心者のうちは、まず「引数で渡すと使える」という動きを確認してもよいでしょう。そのうえで、アプリが大きくなるほど、画面の処理はForm1、データの処理はPlayerという分担を意識すると設計が分かりやすくなります。

よくある誤解:publicにすれば解決する?

label1 をpublicにすれば、どこからでも名前だけで使えるようになるわけではありません。publicは「適切な参照を通して外部からアクセスできる」という意味です。どのForm1インスタンスのlabel1なのかを示す必要があります。

また、コントロールをむやみにpublicにすると、さまざまなクラスが画面を直接変更できるようになり、処理の場所が分かりにくくなります。まずは引数で必要なものだけを渡すか、Form1側でUIを更新する形を検討するとよいでしょう。

まとめ

  • label1 はForm1が持つコントロールである
  • Name はPlayerが持つプロパティである
  • Playerの中から、Form1のメンバーを名前だけで直接参照することはできない
  • 簡単な方法は、Labelをメソッドの引数として渡すこと
  • 役割を分けるなら、Playerはデータを返し、Form1側でLabelを更新する

今回のエラーは、クラスとメンバーの関係、そして変数を参照できる範囲を理解するための良い題材です。「この変数は、どのクラスが持っているのか?」と考える習慣をつけると、同じ種類のエラーを自分で整理しやすくなります。

訪問数 3 回, 今日の訪問数 3回

広告