about_Classes
簡単な説明
クラスを使用して独自のカスタム型を作成する方法について説明します。
長い説明
PowerShell 5.0 では、クラスや他のユーザー定義型を定義するための正式な構文が追加されています。 クラスを追加することで、開発者や IT プロフェッショナルは、より広範な使用事例に対して PowerShell を利用できます。 PowerShell 成果物の開発が簡素化され、管理サーフェスのカバレッジが促進されます。
クラス宣言は、実行時に オブジェクトのインスタンスを作成するために使用されるブループリントです。 クラスを定義する場合、クラス名は型の名前です。 たとえば、Device という名前のクラスを宣言し、変数を Device$dev $dev の新しいインスタンスに初期化する場合、 は Device 型のオブジェクトまたはインスタンス です。 Device の各 インスタンスは 、そのプロパティに異なる値を持つ場合があります。
サポートされるシナリオ
- クラス、プロパティ、メソッド、継承などの使い慣れたオブジェクト指向プログラミング セマンティクスを使用して、PowerShell でカスタム型を定義します。
- PowerShell 言語を使用して型をデバッグします。
- 正式なメカニズムを使用して例外を生成して処理します。
- PowerShell 言語を使用して、DSC リソースと関連する型を定義します。
構文
クラスは、次の構文を使用して宣言されます。
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
クラス プロパティの複合型の例
この例では、Device クラスを 使用して 空の Rack クラス を定義 します。 この例に従って、ラックにデバイスを追加する方法と、事前に読み込まれたラックから始める方法を示します。
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 参照します。 これにより、現在のクラスで定義されているプロパティや他のメソッドにアクセスできます。
プロパティとメソッドを使用した単純なクラスの例
Rack クラスを 拡張 して、デバイスの追加と削除を行います。
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 するには、 を使用する必要があります。
コマンドレットを Write-* 使用すると、引き続きクラス メソッド内から PowerShell の出力ストリームに書き込みできます。 ただし、 メソッドが ステートメントのみを使用してオブジェクトを出力するには、これを避ける必要 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
複数のコンストラクターの例
この例では、 Device クラスは、プロパティ、既定のコンストラクター、およびインスタンスを初期化するコンストラクターを使用して定義されています。
既定のコンストラクターはブランドを Undefined に****設定 し、モデルと vendor-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 は、プロパティまたはメソッドを非表示にしています。 プロパティまたはメソッドはユーザーが引き続きアクセス可能であり、オブジェクトが使用可能なすべてのスコープで使用できます。 非表示のメンバーはコマンドレットから非表示 Get-Member にされ、クラス定義の外部でタブ補完や IntelliSense を使用して表示できない。
詳細については、「About_hidden」 を参照してください。
非表示属性の使用例
Rack オブジェクト が 作成されると、デバイスのスロット数は固定値になります。この値は、いつでも変更することはできません。 この値は作成時に確認されます。
非表示属性を使用すると、開発者はスロットの数を非表示にしておき、ラックのサイズが意図せずに変更されるのを防ぐのに使用できます。
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
Slots プロパティ は出力に表示 $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 クラスでの単純な継承の例
この例では、前の例で使用した Rack クラスと Device クラスの方が、プロパティの繰り返しを回避し、一般的なプロパティの配置を向上し、一般的なビジネス ロジックを再利用する方法に定義されています。
データ センター内のほとんどのオブジェクトは会社の資産です。資産として追跡を開始するのも理にかなっています。 デバイスの種類は 列挙体によって定義 DeviceType されます。列挙 about_Enum の詳細については、「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) からクラスをインポートします。 入れ子になったモジュールで定義されたクラスや、ドットソースのスクリプトで定義されたクラスをモジュールに一貫してインポートすることはできません。 モジュールの外部のユーザーが使用できるクラスは、ルート モジュールで定義する必要があります。
開発中に新しく変更されたコードを読み込む
スクリプト モジュールの開発中に、 Import-Module コードに変更を加え、Force パラメーターを使用して新しいバージョンのモジュールを読み込むのが 一般的 です。 これは、ルート モジュール内の関数に対する変更に対してのみ機能します。 Import-Module では、入れ子になったモジュールは再読み込みされません。 また、更新されたクラスを読み込む方法はありません。
最新バージョンを実行するには、 コマンドレットを使用してモジュールをアンロードする必要 Remove-Module があります。 Remove-Module は、ルート モジュール、入れ子になったすべてのモジュール、およびモジュールで定義されているクラスを削除します。 次に、 と ステートメントを使用して、モジュールとクラス Import-Module を再読み込み using module できます。
もう 1 つの一般的な開発方法は、コードを異なるファイルに分離する方法です。 別のモジュールで定義 using module されたクラスを使用する関数がある場合は、 ステートメントを使用して、関数に必要なクラス定義が含まれています。
PSReference 型は、クラス メンバーではサポートされていません
クラス メンバーで [ref] 型キャストを使用すると、サイレントモードで失敗します。 パラメーターを使用する [ref] API は、クラス メンバーと一緒に使用することはできません。 PSReference クラスは、COM オブジェクトをサポートするように設計されています。 COM オブジェクトには、参照渡しで値を渡す必要がある場合があります。
型の詳細については、「[ref]PSReference クラス」を参照してください。
関連項目
フィードバック
フィードバックの送信と表示