Home > Windows にまつわる e.t.c.

超簡単な PowerShell Class の使い方(その4/継承)


その1 で説明したように、class の良さは class に処理(メソッド)とデータ(プロパティ)をひとまとめに任せることが出来る点にあるのですが、実はもう一つ大きな特徴があります。

それが「継承」です。

 

継承とは、既にある class に手を加えることなく機能の追加/変更ができる仕組みです。

 

function の機能拡張をする場合は、既にある function を書き換えますね。

あるいは、元の function を呼んで追加処理を書き足すといった修正をしますが、その場合は function 名が変わってしまいます。

class の場合は、既にある class に一切手を加える事なく、既にあるメソッドの機能追加や変更、あるいは新しくメソッドを追加することが出来るのです。

(文中で、「内部データ/データ」と「プロパティ」の2つの言葉が出てきますが、文脈によって使い分けているだけで同じ意味です)

 

継承元のメソッドを呼び出す

元となるクラス(ベースクラス)の機能(メソッド)を呼び出す場合は、継承元のクラス名で this をキャストして呼び出します。

[D:\temp\testadd.ps1]

# ベースクラス(元となるクラス)定義
class TestClass30{
    [string] hoge(){
        return "Hello PowerShell Class !!"
    }
}

# サブクラス(継承するクラス)定義
class TestClassMethodAdd : TestClass30 {
    [string] hoge2(){
        $Data = ([TestClass30]$this).hoge()
        $Data += "This is hoge2 !!"
        return $Data
    }
}

# インスタンス化
$TestObject = New-Object TestClassMethodAdd

# メソッドの実行
$TestObject.hoge2()

 

これを実行するとこのようになります。

PS D:\> D:\temp\testadd.ps1
Hello PowerShell Class !!This is hoge2 !!
PS D:\>

 

オーバーライド

元となるクラス(ベースクラス)の機能(メソッド)を、継承するクラス(サブクラス)の同名メソッドで書き換えることを「オーバーライド」と言います。

まずは、機能変更がどうなるのか見てみましょう

[D:\temp\test30.ps1]

# ベースクラス(元となるクラス)定義
class TestClass30{
    [string] hoge(){
        return "Hello PowerShell Class !!"
    }
}

# サブクラス(継承するクラス)定義
class TestClass31 : TestClass30 {
    [string] hoge(){
        return "This is new hoge !!"
    }
}

# インスタンス化
$TestObject = New-Object TestClass31

# メソッドの実行
$TestObject.hoge()

 

これを実行するとこのようになります。

PS D:\> D:\temp\test30.ps1
This is new hoge !!
PS D:\>

 

hoge メソッドが TestClass31 の hoge メソッドに置き換わっていますね。

 

では、継承元のメソッドを利用して機能拡張するケースを見てみましょう。

[D:\temp\test31.ps1]

# ベースクラス定義
class TestClass30{
    [string] hoge(){
        return "Hello PowerShell Class !!"
    }
}

# サブクラス定義
class TestClass32 : TestClass30 {
    [string] hoge(){
        $Result = ([TestClass30]$this).hoge()
        $Data = $Result + " And new hoge !!"
        return $Data
    }
}

# インスタンス化
$TestObject = New-Object TestClass32

# メソッドの実行
$TestObject.hoge()

 

PS D:\> D:\temp\test31.ps1
Hello PowerShell Class !! And new hoge !!
PS D:\>

 

オーバーライドした hoge メソッド内で ([TestClass30]$this).hoge() を使ってベースクラスの hoge を呼び出し、ベースクラスの hoge メソッドの機能拡張できたのがわかりますね。

継承元のメソッドを呼び出す場合は、([BaseClassName]$this) のように $this をベースクラスにキャストしてメソッドを呼び出します。

 

機能追加する場合も想像通りでこんな感じになります。

[D:\temp\test32.ps1]

# ベースクラス定義
class TestClass30{
    [string] hoge(){
        return "Hello PowerShell Class !!"
    }
}

# サブクラス定義
class TestClass33 : TestClass30 {
    
    [string] $NewString = " This is new hoge too !!"

    [string] newhoge(){
        $Result = ([TestClass30]$this).hoge()
        $Data = $Result + $this.NewString
        return $Data
    }
}

# インスタンス化
$TestObject = New-Object TestClass33

# メソッドの実行
$TestObject.newhoge()

 

PS D:\> D:\temp\test32.ps1
Hello PowerShell Class !! This is new hoge too !!
PS D:\>

 

このように、元となるベースクラスには一切手を入れず、継承したサブクラスで機能拡張することが出来ます。

 

コンストラクタのオーバーライド

コンストラクタをオーバーライドする場合は、通常のメソッドを呼ぶのとは異なり「base」でベースクラスのコンストラクタを指定します。

サブクラスで呼ばれるコンストラクタは、インスタンス時の引数に依存し、それに続いて : base() で指定されているベースクラスのコンストラクタが呼ばれます。

コンストラクタが呼び出される順番は、先にベースクラスのコンストラクタが呼ばれ、その後にサブクラスのコンストラクタが呼ばれます。

ぜひ PowerShell ISE でステップ実行して動きを確認してください。(コンストラクタ内で echo とかしても表示されない)

この時のミソは、

・ベースクラスに対応する全てのコンストラクタ作成
・サブクラスでは : base(引数) でベースクラスのコンストラクタを指定
・サブクラスのコンストラクタに渡された引数がそままま : base(引数) に渡され、ベースクラスのコンストラクタが先に実行される
・ベースクラスの内部データは 「([BaseClassName]$this).Property」 でアクセス

する点です。

こうすることで、結果的にサブクラスで内部データへの初期値セットが実現できます。
(コンストラクタは直接呼び出せないので、サブクラスのコンストラクタ内で ([TestClass30]$this) みたいにしてコンストラクタを呼び出してもエラーになります)

 

それでは実際の動きを見てみましょう。

[D:\temp\test33.ps1]

# ベースクラス定義
class TestClass34
{
    # 内部データ
    [int] $Data

    # 初期値なしコンストラクタ
    TestClass34(){

        # 内部データに 0 をセット
        $this.Data = 0
    }

    # 初期値ありコンストラクタ(こちらが呼ばれる)
    TestClass34([int]$Indata){

        # 内部データに初期値をセット
        $this.Data = $Indata
    }

    # 内部データに加算して return するメソッド
    [int] Add([int]$Indata){

        # 内部データに加算
        $this.Data += $Indata

        # 内部データ return
        return $this.Data
    }
}

# サブクラスの定義
class TestClass35 : TestClass34{

    # 引数なしコンストラクタ
    TestClass35() : base() {
    }

    # 引数ありコンストラクタ
    TestClass35([int]$Indata) : base([int]$Indata) {
        # 継承元コンストラクタ処理後にこれが実行される
        ([TestClass34]$this).Data = ([TestClass34]$this).Data + $Indata * 2
    }
}

# インスタンス化(ベースクラスのコントラクタ実行後にサブクラスのコンストラクタが実行される)
$TestObject = New-Object TestClass35(10)

# メソッドの実行(ベースクラスのメソッドを使う)
$TestObject.Add(30)

 

PS D:\> D:\temp\test33.ps1
60
PS D:\>

 

ベースクラスの引数ありコンストラクタは呼ばれず、サブクラスのコントラクタでベースクラスのデータを ([TestClass34]$this).Data で更新しているのがわかると思います。
(ぜひ PowerShell ISE のステップ実行で動きを確認してください)

 

多段継承で大元メソッド呼び出す場合

class を多段で継承している場合、直接継承している class で実装されていないメソッドを使用する時にはどうしたら良いのでしょうか?

メソッドを実装しているクラスを指定してメソッドを呼び出しても良いのですが、直接継承している class のメソッドとして呼び出す事が出来ます。

# 大元クラス
class BaseClass{
    [int] $a = 5

    [int] BaseMethod([int] $input){
        return $this.a + $input
    }
}

# 大元クラスを継承した中間継承クラス
class MiddleClass : BaseClass{

    [int] $b

    [int] MiddleMethod([int] $input){
        return $this.b * $input
    }
}

# 中間継承クラスを継承した末端クラス
class SubClass : MiddleClass{

    [int] SubMethod([int] $input){
        # 大元クラスのメソッドだけど、中間継承クラスのメソッドとして呼び出せる
        [int]$c = ([MiddleClass]$this).BaseMethod( $input ) * 2
        return $c
    }
}

# 中間継承クラスを継承
$TestObject = New-Object SubClass

$TestObject.SubMethod(10)

 

オーバーライドも同様に実装することが出来ます。

ただし、コンストラクタだけは継承したクラス全てで継承元のコンストラクタをオーバーライド( : base() を呼び出すだけであっても )実装しないと、実行時にエラーになるようです。(コンストラクタ引数の数を変更した場合だけかも?)

 

.NET Framework から継承する

PowerShell の class は、.NET Framework から継承した class を定義することが出来ます。

「えー、そんなの滅多に使わないよ」と思う方もいらっしゃるかと思いますが、実は普通に作成した class も.NET Framework から継承されています。

その1で最初に作った class で確認してみます。

# クラス定義
class TestClass{
    [string] hoge(){
        return "Hello PowerShell Class !!"
    }
}

# インスタンス化
$TestObject = New-Object TestClass

echo "メンバー"
$TestObject | Get-Member

echo ""

echo "タイプ"
$TestObject.GetType()

 

PS D:\> # クラス定義
PS D:\> class TestClass{
>>     [string] hoge(){
>>         return "Hello PowerShell Class !!"
>>     }
>> }
PS D:\>
PS D:\> # インスタンス化
PS D:\> $TestObject = New-Object TestClass
PS D:\>
PS D:\> # メンバー
PS D:\> $TestObject | Get-Member


   TypeName: TestClass

Name        MemberType Definition
----        ---------- ----------
Equals      Method     bool Equals(System.Object obj)
GetHashCode Method     int GetHashCode()
GetType     Method     type GetType()
hoge        Method     string hoge()
ToString    Method     string ToString()


PS D:\>
PS D:\> # タイプ
PS D:\> $TestObject.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    TestClass                                System.Object


PS D:\>

 

Get-Member を見ると、作成した hoge メソッド以外にもメソッドが存在していますし、GetType() を見ると BaseType が System.Object になっています。

つまり、作成した TestClass は、.NET Framework の System.Object クラスを継承した class なので、こう書いているのと同じ意味だったのです。

class TestClass : System.Object{
    [string] hoge(){
        return "Hello PowerShell Class !!"
    }
}

 

こんな感じで PowerShell の class は .NET Framework のクラスを継承して、強力なクラスを簡単に作ることが出来るのです。

ここは腕の見せ所ですね

 

関連情報

超簡単な PowerShell Class の使い方(その1)
http://www.vwnet.jp/Windows/PowerShell/2017082001/PSv5Class01.htm

超簡単な PowerShell Class の使い方(その2/オーバーロード)
http://www.vwnet.jp/Windows/PowerShell/2017082101/PSv5Class02.htm

超簡単な PowerShell Class の使い方(その3/static)
http://www.vwnet.jp/Windows/PowerShell/2017082201/PSv5Class03.htm

超簡単な PowerShell Class の使い方(その5/スコープ)
http://www.vwnet.jp/Windows/PowerShell/2017082302/PSv5Class05.htm

超簡単な PowerShell Class の使い方(その6/まとめ)
http://www.vwnet.jp/Windows/PowerShell/2017082401/PSv5Class06.htm

PowerShell Class
http://www.vwnet.jp/Windows/etc.asp#PowerShell_Class

PowerShell クラスを使用したカスタム型の作成Creating Custom Types using PowerShell Classes | Microsoft Docs
https://docs.microsoft.com/ja-jp/powershell/scripting/windows-powershell/wmf/whats-new/class-overview?WT.mc_id=WD-MVP-36880

 

 

back.gif (1980 バイト)

home.gif (1907 バイト)

Copyright © MURA All rights reserved.