スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

F#入門第40回(OOP(5)継承とキャスト)

今回のお題は「OOP(5)継承とキャスト」です。

まずは宿題の解答例からです。
(宿題)
次の暗黙的クラス定義を明示的クラス定義に書き直してください。
type Vec2D (x : float,y:float) =
   
    static let mutable m_addCount = 0 
   
    member this.ShowContents ()
                = printfn "x = %f y = %f" x y
    member this.X = x
    member this.Y = y
     
    member this.Add (t :Vec2D) =
            addCount <- addCount + 1
            new Vec2D(x + t.X,y + t.Y)
   
    static member Vec2DAdd (s:Vec2D,t:Vec2D) =
            addCount <- addCount + 1
            new Vec2D(s.X + t.X,s.Y + t.Y)         
   
    static member AddCountNum =
            m_addCount

(解答例)
type Vec2D  =
    val private _x : float
    val private _y : float
    [<DefaultValue>]
    static val mutable private m_addCount : int
   
    private new (x,y)  =
        {_x = x ; _y = y}
    
    member this.ShowContents() =
        printf "x = %f y = %f " this._x this._y
   
    member this.X = this._x
    member this.Y = this._y
   
    member this.Add (t :Vec2D) =
        Vec2D.m_addCount <- Vec2D.m_addCount + 1
        new Vec2D(this._x + t.X, this._y + t.Y)
    
    static member Vec2DAdd (s:Vec2D,t:Vec2D)=
                     Vec2D.m_addCount <- Vec2D.m_addCount + 1
                     new Vec2D(s.X + t.X, s.Y + t.Y)
   
    static member AddCountNum = Vec2D.m_addCount

さて、今回は継承の復習から始めます。
次のクラス定義をみてください。

[<AbstractClass>]
type HouseAnimal (name : string)=
   
    abstract member Legs :int
    member this.Name = name

ペットを表すクラスを定義してみました。暗黙的クラス定義を用いて、コンストラクタには、ペットの名前を渡します。プロパティLegsをabstractとして、定義してあります。
このプロパティには、実装がないので、このクラスは抽象クラスになります。よって、最初にAbstractClass属性を付け加えてあります。(このクラスのインスタンスは作成できません。)
さて、このクラスから「飼い犬」クラスを派生させます。

type Dog(name) =
    inherit HouseAnimal(name)
    abstract member BarkSound : string
    default this.BarkSound = "???"
    override this.Legs = 4
   
このクラスは、追加のフィールドはなく、追加のプロパティである鳴声(BarkSound)を加え、派生元クラスのLegsプロパティを上書きしています。
さらに犬種別のクラスを二つ付け加えます。

type Corgi (name) =
    inherit Dog(name)
    override this.BarkSound = "バウバウ"


type Siba(name) =
    inherit Dog(name)
    override this.BarkSound = "ワンワン"
   
コーギークラスと柴犬クラスで、派生元クラスであるDogクラスのBarkSoundプロパティを上書きしています。クラス間の親子関係をまとめておくと
Corigiクラスの基底クラス(親クラス)は、DogクラスとAnimalクラス
Shiba クラスの基底クラス(親クラス)は、DogクラスとAnimalクラス
Dogクラスの基底クラス(親クラス)はAnimalクラスとなります。

さてそれではCorgiクラスのインスタンスをひとつと
Shibaクラスのインスタンスをひとつ作ってみます。

> let mayAsCorgi = new Corgi ("メイ");;
val mayAsCorgi : Corgi

> let pochiAsSiba = new Siba ("ポチ");;
val pochiAsSiba : Siba

たとえばこれらをリストに入れようとするとエラーがでます。
> let dLst = [mayAsCorgi;pochiAsSiba];;

  let dLst = [mayAsCorgi;pochiAsSiba];;
  -----------------------^^^^^^^^^^^
C:\Users\T_GYOUTEN\AppData\Local\Temp\stdin(10,24): error FS0001: This expression has type
 Siba but is here used with type Corgi

これは、リストは一種類の型しか要素とできないことに所以します。
たとえば、このような時に使われるのがキャストです。
キャストは基底クラス方向へのキャスト(static upcastと言います)と、その逆方向へのキャスト(Dynamic castと言います)の二種類あります。static upcastは常に成功することが保証されていますが、Dynamic castには、その保証がありません。
static upcast には、:>オペレーター、dynamic castには:?>オペレーターを用います。
?に「できるかどうかわからないな」という匂いを嗅ぎ取ってください。

それでは上の二つのインスタンスを両方の基底クラスである、Dogクラスにキャストしてみます。

> let mayAsDog = mayAsCorgi :> Dog;;
val mayAsDog : Dog

> let pochiAsDog = pochiAsSiba :> Dog;;
val pochiAsDog : Dog

こうしておいてリストを定義してみます。

> let dogLst = [mayAsDog;pochiAsDog];;
val dogLst : Dog list = [FSI_0003+Corgi; FSI_0003+Siba]

こんどはDog型のリストとして定義できました。
それでは吠えてもらいます。

> List.map (fun (d : Dog) -> (printfn "%s" d.BarkSound)) [mayAsDog;pochiAsDog] |> ignore ;;
バウバウ
ワンワン
val it : unit = ()

ignoreをつけてunit型を返すようにしましたが、これ無しなら、リストが返ります。

> List.map (fun (d : Dog) -> (printfn "%s" d.BarkSound)) [mayAsDog;pochiAsDog];;
バウバウ
ワンワン
val it : unit list = [null; null]

それでは、Dynamic castをしてみましょう。

> let k = mayAsDog :?> Corgi;;
val k : Corgi

これは、もともとのインスタンスがCorgiだったので成功します。
それではDogクラスのインスタンスを一つ生成して、CorgiクラスにDynamic Castしてみます。

> let z1 = new Dog ("雑種1号");;
val z1 : Dog

> let mAsCorgi = z1 :?> Corgi ;;

System.InvalidCastException: Unable to cast object of type 'Dog' to type 'Corgi'.
以下略

ということでInvalidCastExceptionがraiseされます。

Dynamic Cast可能かどうかは、:?演算子で次のように調べることができます。

> (mayAsDog :? Corgi);;
val it : bool = true

> (z1 :? Corgi);;
val it : bool = false

また、:?演算子はパターンマッチでもつかえます。

let CanBehaveAsDogOrString (x : obj) =
    match x with
    | :? Dog as d -> printfn "%s can behave as Dog" (d.Name)
    | :? string as s -> printfn "%A can behave as String" s
    | _  -> printfn "%A is %s .So can't behave as Dog nor string" x (x.GetType().Name)

これは、引数がDogまたはstringにdynamic castできるかどうかを調べる関数です。
(Dogかstringという取り合わせが奇妙ですが、ご容赦ください。)

実行例
val CanBehaveAsDogOrString : obj -> unit

> CanBehaveAsDogOrString (mayAsCorgi);;
メイ can behave as Dog
val it : unit = ()

> CanBehaveAsDogOrString ("abc");;
"abc" can behave as String
val it : unit = ()

> CanBehaveAsDogOrString (3);;
3 is Int32 .So can't behave as Dog nor string
val it : unit = ()

スポンサーサイト

コメントの投稿

非公開コメント

プロフィール

T GYOUTEN

Author:T GYOUTEN
F#と英単語とフリーソフトと読書に興味があります。
ホームページでフリーソフトも公開しています。どぞ御贔屓に。

最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
フリーエリア
フリーエリア
blogram投票ボタン
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QRコード
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。