【学習】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 の一種」 と押さえると足がかりになります。Component や Object は、必要になったタイミングで別の記事で触れると負担が少ないです。
プロパティが多い理由
結論はシンプルです。
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 だけ 赤くなります。
このコードで起きていること(分解)
上の foreach + if を、次の3つに分けると説明しやすいです。
Controlでまとめて扱っている(ポリモーフィズムの入り口)this.ControlsはControlの集合で、Button も Label も いったん「Control」として 同じループで回せます。型を揃えて扱う、という感覚です。実体が Button かどうかを調べている
if (c is Button)で、中身の正体 を見ています。共通プロパティを使っている
c.BackColorはControlにあるので、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 の機能を土台として持ったクラス です。プロパティウィンドウを見るたびに 「これは共通? ボタン専用?」 と分ける癖をつけると、あとで楽になります。
次のステップ
- シリーズ続き: WinFormsで学ぶ「継承」入門(シリーズ目次)(
MyButton・共通処理・virtual/overrideによるポリモーフィズムを予定) - 親子と
Controls: WinFormsの「ドラッグ&ドロップ」の正体シリーズ