ラベル 派生クラス の投稿を表示しています。 すべての投稿を表示
ラベル 派生クラス の投稿を表示しています。 すべての投稿を表示

2018年2月23日金曜日

はじめてのWindows Formアプリケーション

今回から、WindowsのFormアプリの学習に入ります。

 Formアプリケーションは、Windows上でウインドウを持つアプリケーションのことです。

昔から持っているイメージは、プログラミングで使う言語の習得よりも、Windowsで走るプログラムのお作法を勉強する方が大変、MFCクラスライブラリの仕様を勉強しなきゃいけなくて大変、っていうもの。

以前Visual C++を買った時には、その辺の敷居が高すぎて、どうにもこうにも学習する気にならなかったものでした。

Formアプリケーション


下記が学習で打ち込んだリストです。Formアプリケーションというと、Visual Studioでデザイナーを使ってウインドウの設計をして・・・・と、普通は考えると思います。

しかし、ウインドウを表示してその中に文字列を表示させる、という程度の動作であれば、デザイナーを使ってウインドウの設計をする、という作業は不要です。下記リストをテキストエディタで打ち込んで、sample.csというファイル名で保存し、Windowsのコマンドプロンプトから

csc sample.cs

と入力すればコンパイル可能で、そのままコマンドプロンプトからsample.exeと入力すればウインドウが現れます。

using System;
using System.Drawing;
using System.Windows.Forms;

namespace MyFrmApp
{
    public class MyForm : Form
    {

        class Program
        {
            [STAThread]
            private static void Main( string[] args )
            {
                Application.Run(new MyForm());
            }
        }

        public MyForm()
        {
            Label label = new Label();
            label.Text = "Welcome to C#!";
            label.Font = new Font("geneva", 18, FontStyle.Regular);
            label.Height = 30;
            label.Width = 300;
            this.Controls.Add(label);
            this.Width = 300;
            this.Height = 200;

        }
    }
}

起動させると、このようなウインドウが出ると思います。

なお、このソースは、Macではライブラリの違いからコンパイルできません。Windowsの.NET Framework環境固有のライブラリを使用しているからです。

さて、今回のプログラムですが、新しい単語が幾つか出てきています。


これらは、System.DrawingとSystem.Windows.Forms名前空間にあるクラスを参照するために記述しています。それぞれの名前空間にどんなものが定義してあるかは、それぞれに付いているリンク先に記述があります。

今回のプログラムで具体的に何をするために記述しているのかというと、System.DrawingはFontクラスを使うために記述しているようです。また、System.Windows.Formsは、Formクラス、Labelクラスを使用する為ですね。


  • [STAThread]


これは、これが書かれたメソッドがシングルスレッドで動くようにする為の属性指定です。.NET Frameworkで動くウインドウは、原則としてシングルスレッドで動くように設計されているとのこと。

この属性指定を行う事で、ウインドウの中で呼び出される処理は、全てウインドウが動いているスレッドの中で処理される、ということだそうです。

参考文献:初心者のためのC#プログラミング入門

このプログラムの仕掛けは、

  • MyFormクラスをFormクラスの派生クラスとして定義
  • MainメソッドでMyFormクラスのインスタンスを生成して、そのインスタンスを起動
  • MyFormクラスのインスタンスが生成されるときに、コンストラクタとしてpublic MyForm()が起動し、その中で
     Labelクラスのインスタンスを生成(名前:label)
     生成したlabelに
      Text属性
      Font属性(これは、Fontクラスのインスタンスを生成して当てる)
      Height属性
      Width属性
     を設定
  • このlabelオブジェクトを、MyFormのControl.Addメソッドを使ってMyFormの上に置く
  • MyFormのWidth属性、Height属性を設定

ということになります。

というわけで、今回はここまで。

2018年2月20日火曜日

オーバーライド、仮想メソッド

今回は、継承の中で、同じ名前で機能を書き換える、オーバーライドのお話です。

オーバーライド


前回のエントリーで、既存のクラスに機能追加を行って、新たなクラスを作る継承について書きました。しかし、継承にはこれだけではなく、派生クラスに、基底クラスと同じ名前で異なったメソッドを持たせる事が出来る機能があります。これをオーバーライドと言います。基底クラスのメソッドを継承クラスで上書きしてしまうものです。オーバーライドするメソッドは、overrideというキーワードを付けて宣言します。

仮想メソッド


オーバーライドされる側のメソッドを仮想メソッドと呼んでいます。メソッドの定義でvirtualというキーワードを付けて宣言します。

下に、学習したときに入力したプログラムリストを示します。

using System;

namespace sample7
{
    class Program
    {
        static void Main(string[] args)
        {
            EnhancedMyObject obj = new EnhancedMyObject();
            obj.name = "Gustav";
            obj.age = 56;
            obj.mail = "mail@truth-maker.com";
            obj.printData();
            Console.ReadKey();
        }
    }

    class MyObject
    {
        public string name = "(noname)";
        public int age = 0;
        public virtual void printData()
        {
            Console.WriteLine("名前 : {0}, 年齢 : {1}歳",name, age);
        }

    }

    class EnhancedMyObject : MyObject
    {
        public string mail = "(no mail)";

        public override void printData()
        {
            Console.WriteLine("名前 : {0}, 年齢 : {1}歳, メール : {2}.", name, age, mail);
        }
    }
}

MyObjectクラスの中で、キーワードvirtual付きで定義されているメソッドが仮想メソッドです。仮想という名前は付いていますが、オーバーライド対象がなければ、このメソッドが実行されます。

EnhancedMyObjectで定義されている、キーワードoverride付きで定義されているメソッドがオーバーライドメソッドです。EnhancedMyObjectクラスはMyObjectクラスの派生クラスですが、printData()メソッドはEnhancedMyObjectクラスで定義されているprintData()メソッドに上書きされてしまいます。

newによるオーバーライド


継承によるオーバーライド、キーワードoverrideによるオーバーライドの他に、キーワードnewによるオーバーライドというものがあります。

using System;

namespace sample8
{
    class Program
    {
        static void Main(string[] args)
        {
            MyObject obj = new EnhancedMyObject();
            obj.name = "Gustav";
            obj.age = 56;
            obj.printData();

            EnhancedMyObject obj2 = (EnhancedMyObject)obj;
            obj2.mail = "mail@truth-maker.com";
            obj2.printData();
            Console.ReadKey();
        }
    }

    class MyObject
    {
        public string name = "(noname)";
        public int age = 0;
        public virtual void printData()
        {
            Console.WriteLine("名前 : {0}, 年齢 : {1}歳",name, age);
        }
    }

    class EnhancedMyObject : MyObject
    {
        public string mail = "(no mail)";
        public new void printData()
        {
            Console.WriteLine("名前 : {0}\n\t 年齢 : {1}歳\n\t メール : {2}.", name, age, mail);
        }
    }
}

上のリストにおいて、基底クラスのメソッドprintData()を派生クラスのprintData()でオーバーライドするのですが、

public new void printData()

という宣言になっています。これを実行すると

MacBook-Pro:sample8 gustav$ mcs Program.cs
MacBook-Pro:sample8 gustav$ mono Program.exe
名前 : Gustav, 年齢 : 56歳
名前 : Gustav
         年齢 : 56歳
         メール : mail@truth-maker.com


となります。

まず、

            MyObject obj = new EnhancedMyObject();

において、MyObject型の変数objを宣言して、EnhancedMyObject型のオブジェクトのインスタンスを生成、その参照をobjに代入します。そのオブジェクトのprintData()メソッドで出力したのが、最初の出力行です。これは、基底クラスで定義しているメソッドの動作です。

次に、

            EnhancedMyObject obj2 = (EnhancedMyObject)obj;

で、EnhancedMyObject型の変数obj2を宣言し、最初にnewで生成したオブジェクトへの参照を保持している変数objをEnhancedMyObject型にキャストしてobj2に代入しています。これは、型の変換はあるものの、同じオブジェクト(最初に生成したEnhancedMyObject型のオブジェクト)への参照です。

しかし、変数obj2が示すオブジェクトのprintData()メソッドで出力したものは、最初の出力と異なり、派生クラスでnew付きで定義したメソッドの動作です。

同じオブジェクトへの参照ですが、変数の型によって動くメソッドが変わっているのです。もしも派生クラスのメソッドがnewではなくてoverrideで定義されていると、出力結果は両方とも同じ、派生クラスで定義されたメソッドの動作になります。

このように、overrideでは、オーバーライドされるメソッドが完全に書き換えられてしまいますが、newでは基底クラスのメソッドも失われることなく残り、同じインスタンスであっても、参照のしかたで使われるメソッドを使い分けることができます。

今回はここまでです。

クラス、そして継承の話

今回はクラスの話になります。

C#はC++とJavaを源流とする言語なんだそうです。ということは、オブジェクト指向であり、クラスという概念を避けて通れません。特にC#では、前回のエントリーにも書いたように、全ての手続きはクラスに属さなければならない決まりになっているので、プログラムを書く=クラスを定義する、ということになります。マイクロソフトのこのドキュメントの二つ目のパラグラフに書いてありますね。

クラス


次のリストは、私がクラスの定義の学習をしたときに使ったプログラムです。

using System;

namespace sample5
{
    class Program
    {
        static void Main(string[] args)
        {
            MyObject obj = new MyObject();
            obj.name = "Gustav";
            obj.age = 56;
            obj.printData();
            Console.ReadKey();
        }
    }

    class MyObject
    {
        public string name = "(noname)";
        public int age = 0;
        public void printData()
        {
            Console.WriteLine("名前 : {0}, 年齢 : {1}歳",name, age);
        }
    }
}
この中では、sample5という名前空間(クラスを整理する為の棚のようなもの)の中に、ProgramというクラスとMyObjectというクラスが定義されています。ProgramクラスにはMainメソッドがありますね。OS上の各種セットアップ処理が終わった後に、プログラム本体の入口としてここに制御が移ります。

下にあるMyObjectクラスの定義では、メンバーの変数やメソッドの定義がなされます。ここで、各メンバーにはpublic宣言がなされているので、MyObjectクラスの外からこれらメンバーを読んだり書いたり呼び出したりできますが、private宣言というものもあり、private宣言がなされたメンバーは、クラスの外からはアクセスできません。

Mainメソッドの1行目で、

MyObject obj = new MyObject();

という記述がされています。

実は、C#ではstatic宣言がされているクラスを除いて、クラスはnew演算子を使って実態を作る(インスタンスを作る)操作をしないと、使うことが出来ません。クラスの宣言は文字通り宣言だけで、実態を作ることで初めて使うことができる、ということになっています。

C++では、new演算子を使うインスタンス生成と使わないインスタンス生成がありますが、C#では、クラスのインスタンス生成には必ずnew演算子を使う決まりになっています。

なお、C++におけるインスタンス生成で、new 演算子を使った場合はオブジェクトがヒープ領域に、使わない場合はスタック領域に生成され、new演算を使わない場合は、スコープから抜けるとオブジェクトは開放されてしまいます。

クラスの継承


既存のクラスの性質を受け継いで、新たな機能を追加したクラスを作ることを継承と言います。派生と言うこともあるようです。このサイト様もよく参考にさせていただいていますけど、ここに詳しい説明があります。

この概念、個人で小規模なプログラミングをやってる人間にはあまりありがたみが分からないのですが、チームで仕様書ベースで開発するようなケースでは有用なのでしょう。また、あらかじめ用意されている、たとえば.NETの環境で、ライブラリーを継承して新たにクラスを作る、なんてときには嬉しい機能なのだと今回の学習で思いました。

なお、継承元のクラスを基底クラス、又はスーパークラス、継承を受ける側を派生クラス、又はサブクラスと呼びます。

次のリストでは、クラスの継承をしています。MyObjectクラスの定義があり、その次にEnchancedMyObjectクラスの定義がありますが、

class EnhancedMyObject : MyObject

の、": MyObject"の記述が、MyObjectクラスを継承することを示しています。

using System;

namespace sample6
{
    class Program
    {
        static void Main(string[] args)
        {
            EnhancedMyObject obj = new EnhancedMyObject();
            obj.name = "Gustav";
            obj.age = 56;
            obj.mail = "mail@truth-maker.com";
            obj.printData();
            obj.printDataEnchanced();
            Console.ReadKey();
        }
    }

    class MyObject
    {
        public string name = "(noname)";
        public int age = 0;
        public void printData()
        {
            Console.WriteLine("名前 : {0}, 年齢 : {1}歳",name, age);
        }
    }

    class EnhancedMyObject : MyObject 
    {
        public string mail = "(no mail)";

        public void printDataEnchanced()
        {
            Console.WriteLine("名前 : {0}\n\t 年齢 : {1}歳\n\t メール : {2}.", name, age, mail );
        }
    }   
}   

Mainメソッドの中で、EnhancedMyObject型のオブジェクトをnew演算子で生成しています。そして、obj.printData()メソッドとobj.printDataEnhanced()メソッドを呼び出しています。

obj.printData()メソッドは、基底クラスに存在しているメソッドなので、派生クラスにも引き継がれ、派生クラスでも使うことが出来ます。プログラムを実行すると、下記のような結果になります。

1行目でソースのコンパイル、2行目で実行させています。

MacBook-Pro:sample6 gustav$ mcs Program.cs
MacBook-Pro:sample6 gustav$ mono Program.exe
名前 : Gustav, 年齢 : 56歳
名前 : Gustav
         年齢 : 56歳
         メール : mail@truth-maker.com.

まず最初に基底クラスから引き継がれたのprintData()メソッドが実行され、次に派生クラスで追加したprintDataEnhanced()メソッドが実行されていますね。

今回はクラスと継承のお話でした。今回はここまで。