about_Classes

簡単な説明

クラスを使用して独自のカスタム型を作成する方法について説明します。

長い説明

PowerShell 5.0 では、クラスや他のユーザー定義型を定義するための正式な構文が追加されています。 クラスを追加することで、開発者や IT プロフェッショナルは、より広範な使用事例に対して PowerShell を利用できます。 PowerShell 成果物の開発が簡素化され、管理サーフェスのカバレッジが促進されます。

クラス宣言は、実行時に オブジェクトのインスタンスを作成するために使用されるブループリントです。 クラスを定義する場合、クラス名は型の名前です。 たとえば 、Device という名前のクラスを宣言し、変数を Device の新しいインスタンスに初期化する場合、 は Device 型のオブジェクトまたは $dev $dev インスタンス です。 Device の各 インスタンスは 、そのプロパティに異なる値を持つ場合があります。

サポートされるシナリオ

  • クラス、プロパティ、メソッド、継承などの使い慣れたオブジェクト指向プログラミング セマンティクスを使用して、PowerShell でカスタム型を定義します。
  • PowerShell 言語を使用して型をデバッグします。
  • 正式なメカニズムを使用して例外を生成して処理します。
  • PowerShell 言語を使用して、DSC リソースと関連する型を定義します。

Syntax

クラスは、次の構文を使用して宣言されます。

class <class-name> [: [<base-class>][,<interface-list]] {
    [[<attribute>] [hidden] [static] <property-definition> ...]
    [<class-name>([<constructor-argument-list>])
      {<constructor-statement-list>} ...]
    [[<attribute>] [hidden] [static] <method-definition> ...]
}

クラスは、次のいずれかの構文を使用してインスタンス化されます。

[$<variable-name> =] New-Object -TypeName <class-name> [
  [-ArgumentList] <constructor-argument-list>]
[$<variable-name> =] [<class-name>]::new([<constructor-argument-list>])

注意

構文を使用 [<class-name>]::new() する場合は、クラス名を囲む角かっこが必須です。 角かっこは、PowerShell の型定義を示します。

構文と使用方法の例

この例では、使用できるクラスを作成するために必要な最小限の構文を示します。

class Device {
    [string]$Brand
}

$dev = [Device]::new()
$dev.Brand = "Microsoft"
$dev
Brand
-----
Microsoft

クラスのプロパティ

プロパティは、クラススコープで宣言された変数です。 プロパティには、任意の組み込み型または別のクラスのインスタンスを指定できます。 クラスに含まれるプロパティの数に制限はありません。

単純なプロパティを持つクラスの例

class Device {
    [string]$Brand
    [string]$Model
    [string]$VendorSku
}

$device = [Device]::new()
$device.Brand = "Microsoft"
$device.Model = "Surface Pro 4"
$device.VendorSku = "5072641000"

$device
Brand     Model         VendorSku
-----     -----         ---------
Microsoft Surface Pro 4 5072641000

クラスプロパティの複合型の例

この例では、デバイス クラスを使用して空の ラック クラスを定義します。 この後の例では、デバイスをラックに追加する方法と、事前に読み込まれたラックから始める方法について説明します。

class Device {
    [string]$Brand
    [string]$Model
    [string]$VendorSku
}

class Rack {
    [string]$Brand
    [string]$Model
    [string]$VendorSku
    [string]$AssetId
    [Device[]]$Devices = [Device[]]::new(8)

}

$rack = [Rack]::new()

$rack

Brand     :
Model     :
VendorSku :
AssetId   :
Devices   : {$null, $null, $null, $null...}


クラスのメソッド

メソッドは、クラスが実行できるアクションを定義します。 メソッドは、入力データを提供するパラメーターを受け取ることができます。 メソッドは出力を返すことができます。 メソッドによって返されるデータは、任意の定義済みデータ型にすることができます。

クラスのメソッドを定義する場合は、自動変数を使用して現在のクラスオブジェクトを参照し $this ます。 これにより、現在のクラスで定義されているプロパティおよびその他のメソッドにアクセスできます。

プロパティとメソッドを持つ単純なクラスの例

ラック クラスを拡張して、デバイスを追加および削除します。

class Device {
    [string]$Brand
    [string]$Model
    [string]$VendorSku

    [string]ToString(){
        return ("{0}|{1}|{2}" -f $this.Brand, $this.Model, $this.VendorSku)
    }
}

class Rack {
    [int]$Slots = 8
    [string]$Brand
    [string]$Model
    [string]$VendorSku
    [string]$AssetId
    [Device[]]$Devices = [Device[]]::new($this.Slots)

    [void] AddDevice([Device]$dev, [int]$slot){
        ## Add argument validation logic here
        $this.Devices[$slot] = $dev
    }

    [void]RemoveDevice([int]$slot){
        ## Add argument validation logic here
        $this.Devices[$slot] = $null
    }

    [int[]] GetAvailableSlots(){
        [int]$i = 0
        return @($this.Devices.foreach{ if($_ -eq $null){$i}; $i++})
    }
}

$rack = [Rack]::new()

$surface = [Device]::new()
$surface.Brand = "Microsoft"
$surface.Model = "Surface Pro 4"
$surface.VendorSku = "5072641000"

$rack.AddDevice($surface, 2)

$rack
$rack.GetAvailableSlots()

Slots     : 8
Brand     :
Model     :
VendorSku :
AssetId   :
Devices   : {$null, $null, Microsoft|Surface Pro 4|5072641000, $null...}

0
1
3
4
5
6
7

クラスのメソッドの出力

メソッドには、戻り値の型が定義されている必要があります。 メソッドが出力を返さない場合、出力の型はになり [void] ます。

クラスのメソッドでは、ステートメントに示されているものを除き、パイプラインにオブジェクトが送信されることはありません return 。 コードからパイプラインへの誤った出力はありません。

注意

これは、PowerShell 関数が出力を処理する方法とは根本的に異なります。ここでは、すべてがパイプラインに送信されます。

クラス メソッド内からエラー ストリームに書き込まれた終了しないエラーは、渡しません。 終了エラーを throw 表示するには、 を使用する必要があります。 コマンドレットを使用すると、引き続きクラス メソッド内から PowerShell の出力 Write-* ストリームに書き込みできます。 ただし、 メソッドが ステートメントのみを使用してオブジェクトを出力するには、これを回避する必要 return があります。

メソッドの出力

この例では、 ステートメントを除き、クラス メソッドからのパイプラインへの誤った出力を示 return します。

class FunWithIntegers
{
    [int[]]$Integers = 0..10

    [int[]]GetOddIntegers(){
        return $this.Integers.Where({ ($_ % 2) })
    }

    [void] GetEvenIntegers(){
        # this following line doesn't go to the pipeline
        $this.Integers.Where({ ($_ % 2) -eq 0})
    }

    [string]SayHello(){
        # this following line doesn't go to the pipeline
        "Good Morning"

        # this line goes to the pipeline
        return "Hello World"
    }
}

$ints = [FunWithIntegers]::new()
$ints.GetOddIntegers()
$ints.GetEvenIntegers()
$ints.SayHello()
1
3
5
7
9
Hello World

コンストラクター

コンストラクターを使用すると、クラスのインスタンスの作成時に既定値を設定し、オブジェクト ロジックを検証できます。 コンストラクターの名前は クラスと同じです。 コンストラクターには、新しい オブジェクトのデータ メンバーを初期化する引数を指定できます。

クラスには、0 個以上のコンストラクターを定義できます。 コンストラクターが定義されていない場合、クラスには既定のパラメーターなしコンストラクターが指定されます。 このコンストラクターは、すべてのメンバーを既定値に初期化します。 オブジェクト型と文字列には null 値が指定されます。 コンストラクターを定義すると、既定のパラメーターなしコンストラクターは作成されません。 パラメーターのないコンストラクターが必要な場合は、コンストラクターを作成します。

コンストラクターの基本的な構文

この例では、Device クラスはプロパティとコンストラクターを使用して定義されています。 このクラスを使用するには、コンストラクターに一覧表示されているパラメーターの値を指定する必要があります。

class Device {
    [string]$Brand
    [string]$Model
    [string]$VendorSku

    Device(
        [string]$b,
        [string]$m,
        [string]$vsk
    ){
        $this.Brand = $b
        $this.Model = $m
        $this.VendorSku = $vsk
    }
}

[Device]$surface = [Device]::new("Microsoft", "Surface Pro 4", "5072641000")

$surface
Brand     Model         VendorSku
-----     -----         ---------
Microsoft Surface Pro 4 5072641000

複数のコンストラクターを使用した例

この例では、 デバイス クラスは、プロパティ、既定のコンストラクター、およびインスタンスを初期化するコンストラクターを使用して定義されています。

既定のコンストラクターは、 ブランドUndefined に設定し、 モデルベンダの sku を null 値にします。

class Device {
    [string]$Brand
    [string]$Model
    [string]$VendorSku

    Device(){
        $this.Brand = 'Undefined'
    }

    Device(
        [string]$b,
        [string]$m,
        [string]$vsk
    ){
        $this.Brand = $b
        $this.Model = $m
        $this.VendorSku = $vsk
    }
}

[Device]$somedevice = [Device]::new()
[Device]$surface = [Device]::new("Microsoft", "Surface Pro 4", "5072641000")

$somedevice
$surface
Brand       Model           VendorSku
-----       -----           ---------
Undefined
Microsoft   Surface Pro 4   5072641000

Hidden 属性

属性は、 hidden プロパティまたはメソッドを非表示にします。 プロパティまたはメソッドは、ユーザーが引き続きアクセスでき、オブジェクトが使用可能なすべてのスコープで使用できます。 非表示のメンバーはコマンドレットによって非表示にされ、 Get-Member タブ補完または IntelliSense を使用してクラス定義の外部に表示することはできません。

詳細については、「 About_hidden」を参照してください。

隠し属性の使用例

ラック オブジェクトが作成されると、デバイスのスロットの数は固定値になり、いつでも変更することはできません。 この値は作成時に認識されます。

開発者は、hidden 属性を使用して、スロットの数を非表示にし、意図しない変更がラックのサイズになるのを防ぐことができます。

class Device {
    [string]$Brand
    [string]$Model
}

class Rack {
    [int] hidden $Slots = 8
    [string]$Brand
    [string]$Model
    [Device[]]$Devices = [Device[]]::new($this.Slots)

    Rack ([string]$b, [string]$m, [int]$capacity){
        ## argument validation here

        $this.Brand = $b
        $this.Model = $m
        $this.Slots = $capacity

        ## reset rack size to new capacity
        $this.Devices = [Device[]]::new($this.Slots)
    }
}

[Rack]$r1 = [Rack]::new("Microsoft", "Surface Pro 4", 16)

$r1
$r1.Devices.Length
$r1.Slots
Brand     Model         Devices
-----     -----         -------
Microsoft Surface Pro 4 {$null, $null, $null, $null...}
16
16

通知 スロット プロパティが出力に表示されません $r1 。 ただし、サイズはコンストラクターによって変更されました。

静的属性

属性は、 static クラスに存在し、インスタンスを必要としないプロパティまたはメソッドを定義します。

静的プロパティは、クラスのインスタンス化とは関係なく、常に使用できます。 静的プロパティは、クラスのすべてのインスタンスで共有されます。 静的メソッドは常に使用できます。 すべての静的プロパティは、セッションスパン全体に対して有効です。

静的属性とメソッドの使用例

ここでインスタンス化されたラックがデータ センターに存在するとします。 そのため、コード内のラックを追跡します。

class Device {
    [string]$Brand
    [string]$Model
}

class Rack {
    hidden [int] $Slots = 8
    static [Rack[]]$InstalledRacks = @()
    [string]$Brand
    [string]$Model
    [string]$AssetId
    [Device[]]$Devices = [Device[]]::new($this.Slots)

    Rack ([string]$b, [string]$m, [string]$id, [int]$capacity){
        ## argument validation here

        $this.Brand = $b
        $this.Model = $m
        $this.AssetId = $id
        $this.Slots = $capacity

        ## reset rack size to new capacity
        $this.Devices = [Device[]]::new($this.Slots)

        ## add rack to installed racks
        [Rack]::InstalledRacks += $this
    }

    static [void]PowerOffRacks(){
        foreach ($rack in [Rack]::InstalledRacks) {
            Write-Warning ("Turning off rack: " + ($rack.AssetId))
        }
    }
}

静的プロパティとメソッドのテストが存在する

PS> [Rack]::InstalledRacks.Length
0

PS> [Rack]::PowerOffRacks()

PS> (1..10) | ForEach-Object {
>>   [Rack]::new("Adatum Corporation", "Standard-16",
>>     $_.ToString("Std0000"), 16)
>> } > $null

PS> [Rack]::InstalledRacks.Length
10

PS> [Rack]::InstalledRacks[3]
Brand              Model       AssetId Devices
-----              -----       ------- -------
Adatum Corporation Standard-16 Std0004 {$null, $null, $null, $null...}

PS> [Rack]::PowerOffRacks()
WARNING: Turning off rack: Std0001
WARNING: Turning off rack: Std0002
WARNING: Turning off rack: Std0003
WARNING: Turning off rack: Std0004
WARNING: Turning off rack: Std0005
WARNING: Turning off rack: Std0006
WARNING: Turning off rack: Std0007
WARNING: Turning off rack: Std0008
WARNING: Turning off rack: Std0009
WARNING: Turning off rack: Std0010

この例を実行すると、ラックの数が増えるので注意してください。

プロパティ検証属性

検証属性を使用すると、プロパティに指定された値が定義された要件を満たしていることをテストできます。 値が割り当てられると、検証がトリガーされます。 「about_functions_advanced_parameters」 を参照してください

検証属性の使用例

class Device {
    [ValidateNotNullOrEmpty()][string]$Brand
    [ValidateNotNullOrEmpty()][string]$Model
}

[Device]$dev = [Device]::new()

Write-Output "Testing dev"
$dev

$dev.Brand = ""
Testing dev

Brand Model
----- -----

Exception setting "Brand": "The argument is null or empty. Provide an
argument that is not null or empty, and then try the command again."
At C:\tmp\Untitled-5.ps1:11 char:1
+ $dev.Brand = ""
+ ~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], SetValueInvocationException
    + FullyQualifiedErrorId : ExceptionWhenSetting

PowerShell クラスでの継承

既存のクラスから派生する新しいクラスを作成することで、クラスを拡張できます。 派生クラスは、基本クラスのプロパティを継承します。 必要に応じて、メソッドとプロパティを追加またはオーバーライドできます。

PowerShell では、複数の継承はサポートされていません。 クラスは、複数のクラスから継承することはできません。 ただし、その目的でインターフェイスを使用できます。

継承の実装は 演算子によって定義されます。つまり、このクラスを拡張するか、これらのインターフェイス : を実装します。 派生クラスは、常にクラス宣言の左端に置く必要があります。

単純な継承構文の使用例

この例は、単純な PowerShell クラスの継承構文を示しています。

Class Derived : Base {...}

この例では、基底クラスの後に来るインターフェイス宣言を使用した継承を示します。

Class Derived : Base, Interface {...}

PowerShell クラスでの単純な継承の例

この例では、前の例で使用した ラック および デバイス クラスが、プロパティの繰り返しを回避し、共通のプロパティをより適切に配置し、一般的なビジネスロジックを再利用するために、より適切に定義されています。

データセンター内のほとんどのオブジェクトは会社の資産であり、資産としての追跡を開始するのに適しています。 デバイスの種類は列挙型によって定義され DeviceType ます。列挙の詳細については、 about_Enum を参照してください。

この例では、とを定義しているだけで、 Rack ComputeServer 両方の拡張機能がクラスに対して定義されてい Device ます。

enum DeviceType {
    Undefined = 0
    Compute = 1
    Storage = 2
    Networking = 4
    Communications = 8
    Power = 16
    Rack = 32
}

class Asset {
    [string]$Brand
    [string]$Model
}

class Device : Asset {
    hidden [DeviceType]$devtype = [DeviceType]::Undefined
    [string]$Status

    [DeviceType] GetDeviceType(){
        return $this.devtype
    }
}

class ComputeServer : Device {
    hidden [DeviceType]$devtype = [DeviceType]::Compute
    [string]$ProcessorIdentifier
    [string]$Hostname
}

class Rack : Device {
    hidden [DeviceType]$devtype = [DeviceType]::Rack
    hidden [int]$Slots = 8

    [string]$Datacenter
    [string]$Location
    [Device[]]$Devices = [Device[]]::new($this.Slots)

    Rack (){
        ## Just create the default rack with 8 slots
    }

    Rack ([int]$s){
        ## Add argument validation logic here
        $this.Devices = [Device[]]::new($s)
    }

    [void] AddDevice([Device]$dev, [int]$slot){
        ## Add argument validation logic here
        $this.Devices[$slot] = $dev
    }

    [void] RemoveDevice([int]$slot){
        ## Add argument validation logic here
        $this.Devices[$slot] = $null
    }
}

$FirstRack = [Rack]::new(16)
$FirstRack.Status = "Operational"
$FirstRack.Datacenter = "PNW"
$FirstRack.Location = "F03R02.J10"

(0..15).ForEach({
    $ComputeServer = [ComputeServer]::new()
    $ComputeServer.Brand = "Fabrikam, Inc."       ## Inherited from Asset
    $ComputeServer.Model = "Fbk5040"              ## Inherited from Asset
    $ComputeServer.Status = "Installed"           ## Inherited from Device
    $ComputeServer.ProcessorIdentifier = "x64"    ## ComputeServer
    $ComputeServer.Hostname = ("r1s" + $_.ToString("000")) ## ComputeServer
    $FirstRack.AddDevice($ComputeServer, $_)
  })

$FirstRack
$FirstRack.Devices
Datacenter : PNW
Location   : F03R02.J10
Devices    : {r1s000, r1s001, r1s002, r1s003...}
Status     : Operational
Brand      :
Model      :

ProcessorIdentifier : x64
Hostname            : r1s000
Status              : Installed
Brand               : Fabrikam, Inc.
Model               : Fbk5040

ProcessorIdentifier : x64
Hostname            : r1s001
Status              : Installed
Brand               : Fabrikam, Inc.
Model               : Fbk5040

<... content truncated here for brevity ...>

ProcessorIdentifier : x64
Hostname            : r1s015
Status              : Installed
Brand               : Fabrikam, Inc.
Model               : Fbk5040

基底クラスのコンストラクターの呼び出し

サブクラスから基底クラスのコンストラクターを呼び出すには、キーワードを追加し base ます。

class Person {
    [int]$Age

    Person([int]$a)
    {
        $this.Age = $a
    }
}

class Child : Person
{
    [string]$School

    Child([int]$a, [string]$s ) : base($a) {
        $this.School = $s
    }
}

[Child]$littleone = [Child]::new(10, "Silver Fir Elementary School")

$littleone.Age

10

基底クラスのメソッドの呼び出し

サブクラスの既存のメソッドをオーバーライドするには、同じ名前とシグネチャを使用してメソッドを宣言します。

class BaseClass
{
    [int]days() {return 1}
}
class ChildClass1 : BaseClass
{
    [int]days () {return 2}
}

[ChildClass1]::new().days()

2

オーバーライドされた実装から基本クラスメソッドを呼び出すには、呼び出し時に基本クラス ([baseclass] $this) にキャストします。

class BaseClass
{
    [int]days() {return 1}
}
class ChildClass1 : BaseClass
{
    [int]days () {return 2}
    [int]basedays() {return ([BaseClass]$this).days()}
}

[ChildClass1]::new().days()
[ChildClass1]::new().basedays()

2
1

継承 (インターフェイスから)

PowerShell クラスは、基底クラスの拡張に使用したのと同じ継承構文を使用してインターフェイスを実装できます。 インターフェイスでは複数の継承が許可されるため、インターフェイスを実装する PowerShell クラスは、型名をコロン () の後に : コンマ () で区切って、複数の型から継承でき , ます。 インターフェイスを実装する PowerShell クラスは、そのインターフェイスのすべてのメンバーを実装する必要があります。 実装インターフェイスのメンバーを省略すると、スクリプトで解析時エラーが発生します。

注意

Powershell では、PowerShell スクリプトでの新しいインターフェイスの宣言は現在サポートされていません。

class MyComparable : System.IComparable
{
    [int] CompareTo([object] $obj)
    {
        return 0;
    }
}

class MyComparableBar : bar, System.IComparable
{
    [int] CompareTo([object] $obj)
    {
        return 0;
    }
}

PowerShell モジュールからのクラスのインポート

Import-Module また、ステートメントでは、モジュール #requires で定義されているモジュール関数、エイリアス、および変数のみをインポートします。 クラスはインポートされません。 ステートメント using module は、モジュールで定義されているクラスをインポートします。 モジュールが現在のセッションに読み込まれなかった場合、ステートメント using は失敗します。 ステートメントの詳細については、「 」 using を参照about_Using。

ステートメント using module は、スクリプト モジュールまたはバイナリ モジュールのルート モジュール ( ModuleToProcess ) からクラスをインポートします。 入れ子になったモジュールで定義されたクラスや、ドットソースのスクリプトで定義されたクラスをモジュールに一貫してインポートすることはできません。 モジュールの外部のユーザーが使用できるクラスは、ルート モジュールで定義する必要があります。

開発中に新しく変更されたコードを読み込む

スクリプト モジュールの開発中に、コードに変更を加え、Force パラメーターを使用して新しいバージョンのモジュールを読み込むのが Import-Module 一般的 です。 これは、ルート モジュール内の関数に対する変更に対してのみ機能します。 Import-Module では、入れ子になったモジュールは再読み込みされません。 また、更新されたクラスを読み込む方法はありません。

最新バージョンを実行するには、 コマンドレットを使用してモジュールをアンロードする必要 Remove-Module があります。 Remove-Module は、ルート モジュール、入れ子になったすべてのモジュール、およびモジュールで定義されているクラスを削除します。 次に、 と ステートメントを使用して、モジュールとクラス Import-Module を再読み込 using module みできます。

もう 1 つの一般的な開発方法は、コードを異なるファイルに分離する方法です。 別のモジュールで定義されたクラスを使用する関数がある場合は、 ステートメントを使用して、関数に必要なクラス定義が含 using module まれています。

PSReference 型は、クラス メンバーではサポートされていません

クラス メンバー [ref] で型キャストを使用すると、サイレントモードで失敗します。 パラメーターを使用する [ref] API は、クラス メンバーと一緒に使用することはできません。 Psreference クラスは、COM オブジェクトをサポートするように設計されています。 COM オブジェクトには、参照によっての値を渡す必要があるケースがあります。

型の詳細については [ref] 、「 Psreference クラス」を参照してください。

こちらもご覧ください