学習記事一覧 · WinFormsで学ぶ継承

【学習】WinFormsで学ぶ「継承」入門(なぜButtonのプロパティはあんなに多いのか)

WinForms で Button を配置すると、プロパティウィンドウに 大量の項目 が表示されます。

  • Size
  • Location
  • BackColor
  • Font
  • Enabled
  • Visible
  • Dock
  • Anchor

「Button ってこんなに機能あったっけ?」と感じたことはないでしょうか。

今回は、その正体が 継承 だと分かると、プロパティの見方や「まとめて同じ操作をする」コードの意味がつながります。

シリーズ目次: WinFormsで学ぶ「継承」入門(シリーズ目次)


今日学ぶこと

  • Button のプロパティが多い理由 … 多くは Control から受け継いでいる
  • 継承 … 別のクラスの性質を引き継ぐこと
  • is-a と has-a … 継承で使えるかどうかの見分け方
  • Control としてまとめて扱うforeach で共通プロパティにアクセスするイメージ
  • is で型を調べる … 必要なときだけ「正体」に応じた処理をする入り口
  • パターン(is Button btn … 型を絞って Button 専用のメンバーに触れる

教材としての段階(意図)

  • 初級Control で型を揃えて、共通のプロパティだけまとめて触れる
  • 中級is で型判定し、Button だけ・Label だけ、といった分岐のイメージをつかむ
  • 上級(この先の回):型の分岐を減らす ポリモーフィズムvirtual / override など)へつなげる

Button を置くとプロパティが大量に見える

プロパティウィンドウを開くと、見た目・配置・有効/無効など、さまざまな項目が並びます。これらの多くは、「Button だから」というより「画面に載る部品(コントロール)だから」 そろっているものです。


Button クラスの正体

実は、Button が 単独で これらすべての機能を持っているわけではありません。

.NET のクラス定義では、おおまかに次のような 派生(継承)の連なり になっています(public class Button : ButtonBase のイメージ)。

public class Button : ButtonBase
{
    // ...
}

さらに遡ると、次のような階層になっています。

Button
 └ ButtonBase
    └ Control
       └ Component
          └ Object

授業や記事では、まず 「Button は Control の一種」 と押さえると足がかりになります。ComponentObject は、必要になったタイミングで別の記事で触れると負担が少ないです。


プロパティが多い理由

結論はシンプルです。

Button は Control クラスの機能をそのまま引き継いでいる

そのうえで、Button らしさ(見た目やクリックの振る舞いなど)が 追加 されています。だからプロパティウィンドウには、「コントロール共通」と「ボタンらしい項目」が 両方 並びます。


Button 自身の機能(ほんの一部)

Button らしいプロパティの例です。

  • Text
  • Image
  • FlatStyle

実際に使っている機能(ほとんどこれ)

次のような項目は、授業の最初のうち とくによく触る 部類です。

  • Size
  • Location
  • BackColor
  • Font
  • Enabled
  • Visible

これらは Button 専用というより、Control 側の機能 として理解できます。


継承とは何か(ここが重要)

継承 とは、

あるクラスが、別のクラスの性質(メンバーや振る舞い)を引き継ぐこと

です。WinForms では「画面に載る部品」が共通の親を持つので、見た目は違っても、土台のプロパティやメソッドが共通 になります。


is-a の関係で考える

継承が成り立つかどうかは、次の一文で判断できます。

「○○ は △△ の一種か?」


正しい例

  • Button is a Control
  • Label is a Control
  • TextBox is a Control

いずれも 「画面に載るコントロールの一種」 として成立します。


間違えやすい例(重要)

次のような例は is-a としては成立しません

  • アプリ is a スマホ ❌

スマホの上でアプリが 動いている のは、所有・利用(中に含まれる・使う) の関係です。

👉 利用(has-a)の関係 と整理します。


整理すると

種類 関係
継承 is-a Button is a Control
利用 has-a アプリ has a スマホ(の上で動く、など)

継承のメリット

同じ Control を継承しているので、Control 型としてまとめて扱える ことが大きなメリットです。


実例コード

フォームに載っている 直下の子コントロール を列挙し、それぞれの背景色を変える例です。

foreach (Control c in this.Controls)
{
    c.BackColor = Color.Red;
}

Controls直下の子だけ です。入れ子になったパネルの中まで一気に辿る話は、別の回(コントロールの親子と列挙)で扱うと整理しやすいです。


何が起きるか

  • Button → 赤になる
  • Label → 赤になる
  • TextBox → 赤になる

型は違っても、Control として同じように BackColor を触れます。


一段進める:Button だけ背景色を変える

「全部まとめて」から一歩進めて、実体が Button のときだけ 赤にする例です。

foreach (Control c in this.Controls)
{
    if (c is Button)
    {
        c.BackColor = Color.Red;
    }
}

Label や TextBox はこの条件に入らないので、Button だけ 赤くなります。


このコードで起きていること(分解)

上の foreachif を、次の3つに分けると説明しやすいです。

  1. Control でまとめて扱っている(ポリモーフィズムの入り口)
    this.ControlsControl の集合で、Button も Label も いったん「Control」として 同じループで回せます。型を揃えて扱う、という感覚です。

  2. 実体が Button かどうかを調べている
    if (c is Button) で、中身の正体 を見ています。

  3. 共通プロパティを使っている
    c.BackColorControl にあるので、Button だと分かったあとも Button にキャストしなくて触れます(この例では c のまま)。


さらに一段:Button 専用のプロパティを触る

Button らしい Text をまとめて変えたいときは、Control 型のままでは「Button だと分かっている」だけでは不十分な場面があります。型を Button に絞る と、意図がはっきりし、Button 専用の API もそのまま使えます。

foreach (Control c in this.Controls)
{
    if (c is Button btn)
    {
        btn.Text = "ボタン";
    }
}

ここでの気づき:列挙するときは Control で足並みを揃える。必要なときだけ is(やパターン)で正体を見て、派生型として扱う、という使い分けです。


試す:Control 型のままでは Button 専用は呼べない

Control 型の変数 c があるとき、次のように コンパイル時に弾かれる 例を一度見ておくと、「型の違い=使えるメンバーの違い」が腹落ちしやすいです。

void TryOnControl(Control c)
{
    c.Text = "変更";              // OK(Control に Text がある)
    c.BackColor = Color.Red;      // OK
    // c.PerformClick();        // NG: Control に PerformClick はない(Button 側)
}

PerformClick のように 派生クラスにしかないメソッド は、Control と宣言された変数からは呼べません。Button として扱うなら、is Button btn のブロックの中で btn.PerformClick() のように書きます。


設計の注意(授業で一言添えるとよい)

if (c is Button) のような 型で分岐する書き方 は、学習ではとても有効ですが、アプリ全体が「is だらけ」になると保守が難しくなることがあります。本番の設計では 分岐を減らす(共通処理を基底に寄せる、ポリモーフィズムで振る舞いを差し替える、など)方向が理想、と一言あると、「理解用のコード」と区別しやすくなります。


なぜこれが重要なのか

違う見た目の部品を、同じ型(の基準)で扱える

これがオブジェクト指向でよく言われる 再利用・拡張・まとめて処理 の入り口です。いま回した foreach は、実務でも 「ボタン1個ずつ名前を書かずに済む」 という意味で重要です。

この記事では is とパターン まで触れました。次の回以降では、MyButton のように 自分でクラスを派生させる 体験や、分岐を減らす ポリモーフィズム の入り口へつなげます。


まとめ

  • Button のプロパティが多い理由の多くは 継承(親クラスから受け継いでいる)
  • よく触る見た目・配置まわりは Control 側の機能 として理解できる
  • 継承は 「is-a」 で判断する
  • has-a(利用) と混同しない
  • 同じ親を持つことで Control としてまとめて扱える
  • 必要なら is / パターン で正体を見て、派生型として Button 専用に触れる
  • 型分岐は 理解のための入口 として有効だが、本番では 減らす設計 が理想、と心に留めておく

授業での一言

見た目は全部バラバラでも、中では同じ型として扱える。でも必要なときだけ正体を見る。

あわせて、Button は「ボタン専用の小さなクラス」だけではなく、Control の機能を土台として持ったクラス です。プロパティウィンドウを見るたびに 「これは共通? ボタン専用?」 と分ける癖をつけると、あとで楽になります。


次のステップ