プロパティの拡張
次にプロパティの拡張について解説します。プロパティを追加してクラスを拡張するのもメソッド同様いたってシンプルです。
type クラス名withで拡張するクラスを指定し、member以降にプロパティ名と値を定義します。
type 拡張するクラス名 with member セルフ識別子.プロパティ名 with get() = get関数ボディ and set(パラメータ) = セット関数ボディ
以下はプロパティによるクラス拡張の例です。
open System type System.Random with //拡張されるクラス名 member this.testProp //プロパティ名 with get() = this.Next(0, 5) //※1 getプロパティの設定 let a = new Random();; //Randomクラスのインスタンス作成 printf "%d" (a.testProp);; //※2
▼
type Random with member testProp : int val a : Random > 4val it : unit = ()
リスト3では、既存のSystem.Randomクラスに、Nextメソッドによる0-5までのランダムな値を取得し(※1)それをgetプロパティ"testProp"として定義しています。
最後に追加されたプロパティを、"printf"で表示(※2)させています。セルフ識別子の部分には通常のメンバーの時同様にローカルでの識別子を指定します。
このプロパティによるクラス拡張機能を使用すれば、例えば、WPFやSilverlightなどの添付プロパティの定義を拡張プロパティとしてクラスの外にまとめ、コントロールにプロパティは定義しながらも拡張プロパティのインポートの有無によってのみ切り替えることなどが可能になります(添付プロパティとは、親オブジェクトで定義されたプロパティに子オブジェクトで別の値を定義するなどして使用できるグローバルなプロパティのことです)。
イベント
メソッド、プロパティだけではなくF#のクラス拡張ではイベントも定義することができますが、クラス拡張の前に、F#のイベントについて基本がまだでしたので、先に説明します。
(1)既存のイベントに動作を追加
既存のイベントに動作を追加するには以下のように行います。
既存のイベント.Add(イベントハンドラ)
以下の例では、form.Clickイベントにクリックされたらフォームを閉じるというハンドラを追加しています。
open System.Windows.Forms let form = new Form(Text="F# Windows Form for Event Test", Visible = true, TopMost = true) form.Click.Add(fun _ -> form.Close()) //イベントハンドラの追加 Application.Run(form)
Addメソッドに渡すイベントハンドラメソッドはパラメータを1つ受け取り、unitを返します。下記の例ではイベントハンドラの部分をラムダ式で表していますが、ラムダ式の代わりに関数名を使用することも可能です。
(2)イベントの新規作成
以下はC#によるイベント新規作成の例です。
public class testEventArgs : EventArgs//※4 EventArgsを継承したパラメター用のクラス作成 { public string triggerParam; } public delegate void testEvHand(object x, testEventArgs y); //デリゲートの宣言 class Class { public event testEvHand testEvent; //※1 イベント本体(testEvent)の定義 public void testHandler( object x, testEventArgs y ) //※2 イベントハンドラの定義 { Console.WriteLine(y.triggerParam); } static void Main(string[] args) { Class testObj1 = new Class(); testObj1.testEvent += new testEvHand(testObj1.testHandler); //イベントハンドラの追加 testEventArgs testObj2 = new testEventArgs(); //パラメータ用クラスのインスタンス testObj2.triggerParam = "Event : Triggerd"; //※3 パラメータとして渡したい情報を記録 testObj1.testEvent( testObj1, testObj2 );// ※5 イベントを呼び出しパラメータを渡す。 } }
上の例では、イベントtestEvent(※1)とそのイベントハンドラtestEvHandの定義(※2)し、さらにそのイベントに引数を渡すようにしています。
イベントに引数を渡すために、Sytem.EventArgsクラスを継承したパラメータを記録(※3)するためのクラスを作成(※4)し、そのクラスのインスタンス経由でパラメータを渡し(※5)ています。
次に同様に新規イベント作成の処理をF#で行ってみましょう。まず、構文から説明します。
typeクラス名() = let組名 = new Event<_>() //イベントハンドラの追加 member セルフ識別子.イベント名 = Event.add (イベントハンドラ) 組名.Publish Event.add (イベントハンドラ) 組名.Publish … //トリガー定義 member セルフ識別子.トリガー名(パラメータ) = 組名.Trigger(パラメータ) //イベント呼び出し letインスタンス名 = クラス名() インスタンス名.イベント名 インスタンス名.トリガー名(パラメータ)
Event関数は2つの要素から構成される組(タプル)を作成します。1つ目の要素は、トリガー(イベントを発生させる物)用のフィールドで、2つ目の要素にはイベントデータ用の値が入ります。
その後に、イベントハンドラとトリガーを定義します。トリガーには1つ引数を与えることができます。イベントは、イベント用に作成したクラスのインスタンス経由でイベントを呼び出し、その後、トリガーに引数を渡します。
では、先ほどC#イベントを新規作成したようにF#でもイベントを作成してみましょう。
type testClass() = let testTuple = new Event<_>() //※1 testTuple組は二つのフィールドからなる member this.testEvent() = //※2 イベントハンドラ追加し、イベントを発行 Event.add(fun str -> printfn "%s" str) testTuple.Publish member this.testTrigger(a) =//※3トリガー定義 testTuple.Trigger(a) //※4 イベントの呼び出し let testObj = testClass() testObj.testEvent() testObj.testTrigger("Event : Triggered") //※5 パラメータを渡す
▼
Event : Triggered type testClass = class new : unit -> testClass member testEvent : unit -> unit member testTrigger : a:string -> unit end val testObj : testClass
F#では、直接パラメータを渡すことが可能で、新たなクラスを作成する必要がありません。
まず、トリガー用とイベント用のフィールドを持つ組testTupleをnew Eventで作成します(※1)。memberキーワードとEvent.addメソッドでイベントハンドラを追加(※2)して、その後トリガーを定義(※3)します。例で設定してあるイベントハンドラは引数としてイベントに渡された値を表示するというものです。イベントの呼び出(※4)しは、イベント用クラスtestClassのインスタンス経由でイベント(testEvent)とトリガー(testTrigger)を呼び、そのときにパラメータを渡します(※5)。