次の方法で共有


about_Classes_Inheritance

簡単な説明

他の型を拡張するクラスを定義する方法について説明します。

長い説明

PowerShell クラスでは、継承がサポートされています。これにより、親クラスの動作を再利用 (継承)、拡張、または変更する子クラスを定義できます。 メンバーが継承されるクラスは、基底クラスと呼ばれます。 基底クラスのメンバーを継承するクラスは、派生クラスと呼ばれます。

PowerShell では、単一継承のみがサポートされます。 クラスは、1 つのクラスからのみ継承できます。 ただし、継承は推移的であるため、一連の型の継承階層を定義できます。 言い換えると、型 D は、型 Cから継承し、この型は Bから継承し、さらにこの型は基底クラスの型 Aから継承します。継承は推移的であるため、型 A のメンバーは型 Dで利用可能です。

派生クラスは、基底クラスのすべてのメンバーを継承するわけではありません。 次のメンバーは継承されません。

  • クラスの静的データを初期化する静的コンストラクター。
  • インスタンス コンストラクター。クラスの新しいインスタンスを作成するために呼び出します。 各クラスは、独自のコンストラクターを定義する必要があります。

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

クラスは、コントラクトを定義するインターフェイスから継承することもできます。 インターフェイスから継承するクラスは、そのコントラクトを実装する必要があります。 その場合、クラスは、そのインターフェイスを実装する他のクラスと同様に使用できます。 クラスがインターフェイスから継承してもインターフェイスを実装していない場合、PowerShell はクラスの解析エラーを発生させます。

一部の PowerShell 演算子は、特定のインターフェイスを実装するクラスに依存します。 たとえば、-eq 演算子は、クラスが System.IEquatable インターフェイスを実装しない限り、参照の等価性のみをチェックします。 -le-lt-ge、および -gt 演算子は、System.IComparable インターフェイスを実装するクラスでのみ機能します。

派生クラスでは、: 構文を使用して基底クラスを拡張するか、インターフェイスを実装します。 派生クラスは、常にクラス宣言の左端に配置する必要があります。

この例では、基本的な PowerShell クラスの継承構文を示します。

class Derived : Base {...}

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

class Derived : Base, Interface {...}

構文

クラスの継承では、次の構文が使用されます。

1 行の構文

class <derived-class-name> : <base-class-or-interface-name>[, <interface-name>...] {
    <derived-class-body>
}

例えば:

# Base class only
class Derived : Base {...}
# Interface only
class Derived : System.IComparable {...}
# Base class and interface
class Derived : Base, System.IComparable {...}

複数行構文

class <derived-class-name> : <base-class-or-interface-name>[,
    <interface-name>...] {
    <derived-class-body>
}

例えば:

class Derived : Base,
                System.IComparable,
                System.IFormattable,
                System.IConvertible {
    # Derived class definition
}

例示

例 1 - 基底クラスの継承とオーバーライド

次の例は、オーバーライドの有無に関係なく、継承されたプロパティの動作を示しています。 説明を読んだ後、コード ブロックを順番に実行します。

基底クラスの定義

最初のコード ブロックは、publishedWork 基底クラスとして定義します。 リストアーティストの 2 つの静的プロパティがあります。 次に、静的 RegisterWork() メソッドを定義して、静的 List プロパティに作品を追加し、アーティストを Artists プロパティに追加し、リスト内の新しいエントリごとにメッセージを書き込みます。

このクラスは、発行された作業を記述する 3 つのインスタンス プロパティを定義します。 最後に、Register() および ToString() インスタンス メソッドを定義します。

class PublishedWork {
    static [PublishedWork[]] $List    = @()
    static [string[]]        $Artists = @()

    static [void] RegisterWork([PublishedWork]$Work) {
        $wName   = $Work.Name
        $wArtist = $Work.Artist
        if ($Work -notin [PublishedWork]::List) {
            Write-Verbose "Adding work '$wName' to works list"
            [PublishedWork]::List += $Work
        } else {
            Write-Verbose "Work '$wName' already registered."
        }
        if ($wArtist -notin [PublishedWork]::Artists) {
            Write-Verbose "Adding artist '$wArtist' to artists list"
            [PublishedWork]::Artists += $wArtist
        } else {
            Write-Verbose "Artist '$wArtist' already registered."
        }
    }

    static [void] ClearRegistry() {
        Write-Verbose "Clearing PublishedWork registry"
        [PublishedWork]::List    = @()
        [PublishedWork]::Artists = @()
    }

    [string] $Name
    [string] $Artist
    [string] $Category

    [void] Init([string]$WorkType) {
        if ([string]::IsNullOrEmpty($this.Category)) {
            $this.Category = "${WorkType}s"
        }
    }

    PublishedWork() {
        $WorkType = $this.GetType().FullName
        $this.Init($WorkType)
        Write-Verbose "Defined a published work of type [$WorkType]"
    }

    PublishedWork([string]$Name, [string]$Artist) {
        $WorkType    = $this.GetType().FullName
        $this.Name   = $Name
        $this.Artist = $Artist
        $this.Init($WorkType)

        Write-Verbose "Defined '$Name' by $Artist as a published work of type [$WorkType]"
    }

    PublishedWork([string]$Name, [string]$Artist, [string]$Category) {
        $WorkType    = $this.GetType().FullName
        $this.Name   = $Name
        $this.Artist = $Artist
        $this.Init($WorkType)

        Write-Verbose "Defined '$Name' by $Artist ($Category) as a published work of type [$WorkType]"
    }

    [void]   Register() { [PublishedWork]::RegisterWork($this) }
    [string] ToString() { return "$($this.Name) by $($this.Artist)" }
}

オーバーライドなしで派生クラスを定義する

最初の派生クラスは Albumです。 プロパティやメソッドはオーバーライドされません。 基本クラスに存在しない新しいインスタンス プロパティ Genresが追加されます。

class Album : PublishedWork {
    [string[]] $Genres   = @()
}

次のコード ブロックは、派生 Album クラスの動作を示しています。 まず、クラス メソッドからのメッセージがコンソールに出力されるように、$VerbosePreference を設定します。 クラスの 3 つのインスタンスが作成され、テーブルに表示された後、継承された静的 RegisterWork() メソッドに登録されます。 その後、基底クラスで同じ静的メソッドを直接呼び出します。

$VerbosePreference = 'Continue'
$Albums = @(
    [Album]@{
        Name   = 'The Dark Side of the Moon'
        Artist = 'Pink Floyd'
        Genres = 'Progressive rock', 'Psychedelic rock'
    }
    [Album]@{
        Name   = 'The Wall'
        Artist = 'Pink Floyd'
        Genres = 'Progressive rock', 'Art rock'
    }
    [Album]@{
        Name   = '36 Chambers'
        Artist = 'Wu-Tang Clan'
        Genres = 'Hip hop'
    }
)

$Albums | Format-Table
$Albums | ForEach-Object { [Album]::RegisterWork($_) }
$Albums | ForEach-Object { [PublishedWork]::RegisterWork($_) }
VERBOSE: Defined a published work of type [Album]
VERBOSE: Defined a published work of type [Album]
VERBOSE: Defined a published work of type [Album]

Genres                               Name                      Artist       Category
------                               ----                      ------       --------
{Progressive rock, Psychedelic rock} The Dark Side of the Moon Pink Floyd   Albums
{Progressive rock, Art rock}         The Wall                  Pink Floyd   Albums
{Hip hop}                            36 Chambers               Wu-Tang Clan Albums

VERBOSE: Adding work 'The Dark Side of the Moon' to works list
VERBOSE: Adding artist 'Pink Floyd' to artists list
VERBOSE: Adding work 'The Wall' to works list
VERBOSE: Artist 'Pink Floyd' already registered.
VERBOSE: Adding work '36 Chambers' to works list
VERBOSE: Adding artist 'Wu-Tang Clan' to artists list

VERBOSE: Work 'The Dark Side of the Moon' already registered.
VERBOSE: Artist 'Pink Floyd' already registered.
VERBOSE: Work 'The Wall' already registered.
VERBOSE: Artist 'Pink Floyd' already registered.
VERBOSE: Work '36 Chambers' already registered.
VERBOSE: Artist 'Wu-Tang Clan' already registered.

Album クラスで Category またはコンストラクターの値が定義されていない場合でも、プロパティは基底クラスの既定のコンストラクターによって定義されていることに注意してください。

詳細メッセージングでは、RegisterWork() メソッドの 2 回目の呼び出しで、作品とアーティストが既に登録されていることを報告します。 RegisterWork() の最初の呼び出しは、派生した Album クラスに対するものでしたが、基底 PublishedWork クラスから継承された静的メソッドを使用しました。 このメソッドは、派生クラスがオーバーライドしなかった基底クラスの静的 List プロパティと Artist プロパティを更新しました。

次のコード ブロックは、レジストリをクリアし、Register() オブジェクトの インスタンス メソッドを呼び出します。

[PublishedWork]::ClearRegistry()
$Albums.Register()
VERBOSE: Clearing PublishedWork registry

VERBOSE: Adding work 'The Dark Side of the Moon' to works list
VERBOSE: Adding artist 'Pink Floyd' to artists list
VERBOSE: Adding work 'The Wall' to works list
VERBOSE: Artist 'Pink Floyd' already registered.
VERBOSE: Adding work '36 Chambers' to works list
VERBOSE: Adding artist 'Wu-Tang Clan' to artists list

Album オブジェクトのインスタンス メソッドは、派生クラスまたは基底クラスで静的メソッドを呼び出すのと同じ効果があります。

次のコード ブロックは、基底クラスと派生クラスの静的プロパティを比較し、それらが同じであることを示します。

[pscustomobject]@{
    '[PublishedWork]::List'    = [PublishedWork]::List -join ",`n"
    '[Album]::List'            = [Album]::List -join ",`n"
    '[PublishedWork]::Artists' = [PublishedWork]::Artists -join ",`n"
    '[Album]::Artists'         = [Album]::Artists -join ",`n"
    'IsSame::List'             = (
        [PublishedWork]::List.Count -eq [Album]::List.Count -and
        [PublishedWork]::List.ToString() -eq [Album]::List.ToString()
    )
    'IsSame::Artists'          = (
        [PublishedWork]::Artists.Count -eq [Album]::Artists.Count -and
        [PublishedWork]::Artists.ToString() -eq [Album]::Artists.ToString()
    )
} | Format-List
[PublishedWork]::List    : The Dark Side of the Moon by Pink Floyd,
                           The Wall by Pink Floyd,
                           36 Chambers by Wu-Tang Clan
[Album]::List            : The Dark Side of the Moon by Pink Floyd,
                           The Wall by Pink Floyd,
                           36 Chambers by Wu-Tang Clan
[PublishedWork]::Artists : Pink Floyd,
                           Wu-Tang Clan
[Album]::Artists         : Pink Floyd,
                           Wu-Tang Clan
IsSame::List             : True
IsSame::Artists          : True

オーバーライドを使用した派生クラスの定義

次のコード ブロックでは、基本 PublishedWork クラスから継承する Illustration クラスを定義します。 新しいクラスは、Medium インスタンス プロパティを既定値の Unknownで定義することで、基底クラスを拡張します。

派生 Album クラスとは異なり、 は次のプロパティとメソッドをオーバーライドします。

  • 静的 Artists プロパティをオーバーライドします。 定義は同じですが、Illustration クラスで直接宣言されます。
  • Category インスタンス プロパティをオーバーライドし、既定値を Illustrationsに設定します。
  • ToString() インスタンス メソッドをオーバーライドして、図の文字列形式に作成されたメディアが含まれるようにします。

また、クラスは静的 RegisterIllustration() メソッドを定義して、最初に基底クラス RegisterWork() メソッドを呼び出し、その後、オーバーライドされた Artists 派生クラスの静的プロパティにアーティストを追加します。

最後に、このクラスは次の 3 つのコンストラクターをすべてオーバーライドします。

  1. 図を作成したことを示す詳細メッセージを除き、既定のコンストラクターは空です。
  2. 次のコンストラクターは、図を作成した名前とアーティストの 2 つの文字列値を受け取ります。 Name プロパティと Artist プロパティを設定するためのロジックを実装する代わりに、コンストラクターは基底クラスから適切なコンストラクターを呼び出します。
  3. 最後のコンストラクターは、図の名前、アーティスト、およびメディアの 3 つの文字列値を受け取ります。 どちらのコンストラクターも、図を作成したことを示す詳細メッセージを書き込みます。
class Illustration : PublishedWork {
    static [string[]] $Artists = @()

    static [void] RegisterIllustration([Illustration]$Work) {
        $wArtist = $Work.Artist

        [PublishedWork]::RegisterWork($Work)

        if ($wArtist -notin [Illustration]::Artists) {
            Write-Verbose "Adding illustrator '$wArtist' to artists list"
            [Illustration]::Artists += $wArtist
        } else {
            Write-Verbose "Illustrator '$wArtist' already registered."
        }
    }

    [string] $Category = 'Illustrations'
    [string] $Medium   = 'Unknown'

    [string] ToString() {
        return "$($this.Name) by $($this.Artist) ($($this.Medium))"
    }

    Illustration() {
        Write-Verbose 'Defined an illustration'
    }

    Illustration([string]$Name, [string]$Artist) : base($Name, $Artist) {
        Write-Verbose "Defined '$Name' by $Artist ($($this.Medium)) as an illustration"
    }

    Illustration([string]$Name, [string]$Artist, [string]$Medium) {
        $this.Name = $Name
        $this.Artist = $Artist
        $this.Medium = $Medium

        Write-Verbose "Defined '$Name' by $Artist ($Medium) as an illustration"
    }
}

次のコード ブロックは、派生 クラスの動作を示しています。 クラスの 3 つのインスタンスが作成され、テーブルに表示された後、継承された静的 RegisterWork() メソッドに登録されます。 その後、基底クラスで同じ静的メソッドを直接呼び出します。 最後に、基底クラスと派生クラスの登録済みアーティストの一覧を示すメッセージを書き込みます。

$Illustrations = @(
    [Illustration]@{
        Name   = 'The Funny Thing'
        Artist = 'Wanda Gág'
        Medium = 'Lithography'
    }
    [Illustration]::new('Millions of Cats', 'Wanda Gág')
    [Illustration]::new(
      'The Lion and the Mouse',
      'Jerry Pinkney',
      'Watercolor'
    )
)

$Illustrations | Format-Table
$Illustrations | ForEach-Object { [Illustration]::RegisterIllustration($_) }
$Illustrations | ForEach-Object { [PublishedWork]::RegisterWork($_) }
"Published work artists: $([PublishedWork]::Artists -join ', ')"
"Illustration artists: $([Illustration]::Artists -join ', ')"
VERBOSE: Defined a published work of type [Illustration]
VERBOSE: Defined an illustration
VERBOSE: Defined 'Millions of Cats' by Wanda Gág as a published work of type [Illustration]
VERBOSE: Defined 'Millions of Cats' by Wanda Gág (Unknown) as an illustration
VERBOSE: Defined a published work of type [Illustration]
VERBOSE: Defined 'The Lion and the Mouse' by Jerry Pinkney (Watercolor) as an illustration

Category      Medium      Name                   Artist
--------      ------      ----                   ------
Illustrations Lithography The Funny Thing        Wanda Gág
Illustrations Unknown     Millions of Cats       Wanda Gág
Illustrations Watercolor  The Lion and the Mouse Jerry Pinkney

VERBOSE: Adding work 'The Funny Thing' to works list
VERBOSE: Adding artist 'Wanda Gág' to artists list
VERBOSE: Adding illustrator 'Wanda Gág' to artists list
VERBOSE: Adding work 'Millions of Cats' to works list
VERBOSE: Artist 'Wanda Gág' already registered.
VERBOSE: Illustrator 'Wanda Gág' already registered.
VERBOSE: Adding work 'The Lion and the Mouse' to works list
VERBOSE: Adding artist 'Jerry Pinkney' to artists list
VERBOSE: Adding illustrator 'Jerry Pinkney' to artists list

VERBOSE: Work 'The Funny Thing' already registered.
VERBOSE: Artist 'Wanda Gág' already registered.
VERBOSE: Work 'Millions of Cats' already registered.
VERBOSE: Artist 'Wanda Gág' already registered.
VERBOSE: Work 'The Lion and the Mouse' already registered.
VERBOSE: Artist 'Jerry Pinkney' already registered.

Published work artists: Pink Floyd, Wu-Tang Clan, Wanda Gág, Jerry Pinkney

Illustration artists: Wanda Gág, Jerry Pinkney

インスタンスの作成時の詳細なメッセージによって、次のことが示されています。

  • 最初のインスタンスを作成するときに、基底クラスの既定のコンストラクターは、派生クラスの既定のコンストラクターの前に呼び出されました。
  • 2 番目のインスタンスを作成するときに、明示的に継承されたコンストラクターは、派生クラス コンストラクターの前に基底クラスに対して呼び出されました。
  • 3 番目のインスタンスを作成するときに、基底クラスの既定のコンストラクターが派生クラス コンストラクターの前に呼び出されました。

RegisterWork() メソッドからの詳細メッセージは、作品とアーティストが既に登録されていることを示しています。 これは、RegisterIllustration() メソッドが内部的に RegisterWork() メソッドを呼び出したためです。

ただし、基底クラスと派生クラスの両方の静的 Artist プロパティの値を比較すると、値は異なります。 派生クラスの Artists プロパティには、アルバム アーティストではなく、イラストレータのみが含まれます。 派生クラスで Artist プロパティを再定義すると、基底クラスの静的プロパティがクラスから返されなくなります。

最後のコード ブロックは、基底クラスの静的 ToString() プロパティのエントリに対して メソッドを呼び出します。

[PublishedWork]::List | ForEach-Object -Process { $_.ToString() }
The Dark Side of the Moon by Pink Floyd
The Wall by Pink Floyd
36 Chambers by Wu-Tang Clan
The Funny Thing by Wanda Gág (Lithography)
Millions of Cats by Wanda Gág (Unknown)
The Lion and the Mouse by Jerry Pinkney (Watercolor)

Album インスタンスは、文字列内の名前とアーティストのみを返します。 イラスト インスタンスには、かっこ内の媒体も含まれています。これは、そのクラスが ToString() メソッドをオーバーライドしたためです。

例 2 - インターフェイスの実装

次の例は、クラスが 1 つ以上のインターフェイスを実装する方法を示しています。 この例では、より多くの操作と動作をサポートするために、Temperature クラスの定義を拡張します。

初期クラス定義

インターフェイスを実装する前に、Temperature クラスは、DegreesScaleの 2 つのプロパティで定義されます。 これは、特定のスケールの程度としてインスタンスを返すコンストラクターと 3 つのインスタンス メソッドを定義します。

このクラスは、TemperatureScale 列挙型を使用して使用可能なスケールを定義します。

class Temperature {
    [float]            $Degrees
    [TemperatureScale] $Scale

    Temperature() {}
    Temperature([float] $Degrees)          { $this.Degrees = $Degrees }
    Temperature([TemperatureScale] $Scale) { $this.Scale = $Scale }
    Temperature([float] $Degrees, [TemperatureScale] $Scale) {
        $this.Degrees = $Degrees
        $this.Scale   = $Scale
    }

    [float] ToKelvin() {
        switch ($this.Scale) {
            Celsius    { return $this.Degrees + 273.15 }
            Fahrenheit { return ($this.Degrees + 459.67) * 5/9 }
        }
        return $this.Degrees
    }
    [float] ToCelsius() {
        switch ($this.Scale) {
            Fahrenheit { return ($this.Degrees - 32) * 5/9 }
            Kelvin     { return $this.Degrees - 273.15 }
        }
        return $this.Degrees
    }
    [float] ToFahrenheit() {
        switch ($this.Scale) {
            Celsius    { return $this.Degrees * 9/5 + 32 }
            Kelvin     { return $this.Degrees * 9/5 - 459.67 }
        }
        return $this.Degrees
    }
}

enum TemperatureScale {
    Celsius    = 0
    Fahrenheit = 1
    Kelvin     = 2
}

ただし、この基本的な実装では、次の出力例に示すようにいくつかの制限があります。

$Celsius    = [Temperature]::new()
$Fahrenheit = [Temperature]::new([TemperatureScale]::Fahrenheit)
$Kelvin     = [Temperature]::new(0, 'Kelvin')

$Celsius, $Fahrenheit, $Kelvin

"The temperatures are: $Celsius, $Fahrenheit, $Kelvin"

[Temperature]::new() -eq $Celsius

$Celsius -gt $Kelvin
Degrees      Scale
-------      -----
   0.00    Celsius
   0.00 Fahrenheit
   0.00     Kelvin

The temperatures are: Temperature, Temperature, Temperature

False

InvalidOperation:
Line |
  11 |  $Celsius -gt $Kelvin
     |  ~~~~~~~~~~~~~~~~~~~~
     | Cannot compare "Temperature" because it is not IComparable.

出力では、Temperature のインスタンスについて、以下のことが示されています。

  • 文字列として正しく表示されません。
  • 等価性を正しくチェックできません。
  • 比較できません。

この 3 つの問題は、クラスのインターフェイスを実装することで解決できます。

IFormattable の実装

Temperature クラスに実装する最初のインターフェイスは、System.IFormattableです。 このインターフェイスを使用すると、クラスのインスタンスを異なる文字列として書式設定できます。 インターフェイスを実装するには、クラス System.IFormattable から継承し、ToString() インスタンス メソッドを定義する必要があります。

ToString() インスタンス メソッドには、次のシグネチャが必要です。

[string] ToString(
    [string]$Format,
    [System.IFormatProvider]$FormatProvider
) {
    # Implementation
}

インターフェイスに必要な署名は、リファレンス ドキュメントに記載されています。

Temperatureの場合、クラスは 3 つの形式をサポートする必要があります。インスタンスを摂氏で返す C、華氏で返す F、ケルビンで返す K。 その他の形式の場合、メソッドは System.FormatException をスローする必要があります。

[string] ToString(
    [string]$Format,
    [System.IFormatProvider]$FormatProvider
) {
    # If format isn't specified, use the defined scale.
    if ([string]::IsNullOrEmpty($Format)) {
        $Format = switch ($this.Scale) {
            Celsius    { 'C' }
            Fahrenheit { 'F' }
            Kelvin     { 'K' }
        }
    }
    # If format provider isn't specified, use the current culture.
    if ($null -eq $FormatProvider) {
        $FormatProvider = [cultureinfo]::CurrentCulture
    }
    # Format the temperature.
    switch ($Format) {
        'C' {
            return $this.ToCelsius().ToString('F2', $FormatProvider) + '°C'
        }
        'F' {
            return $this.ToFahrenheit().ToString('F2', $FormatProvider) + '°F'
        }
        'K' {
            return $this.ToKelvin().ToString('F2', $FormatProvider) + '°K'
        }
    }
    # If we get here, the format is invalid.
    throw [System.FormatException]::new(
        "Unknown format: '$Format'. Valid Formats are 'C', 'F', and 'K'"
    )
}

この実装では、数値の度数値自体を書式化するときに、書式のインスタンス スケールと現在のカルチャがメソッドの既定値として使用されています。 To<Scale>() インスタンス メソッドを使用して度を変換し、小数点以下 2 桁に書式設定し、適切な度記号を文字列に追加します。

必要なシグネチャを実装すると、クラスでオーバーロードを定義して、書式設定されたインスタンスを簡単に返すこともできます。

[string] ToString([string]$Format) {
    return $this.ToString($Format, $null)
}

[string] ToString() {
    return $this.ToString($null, $null)
}

次のコードは、Temperatureの更新された定義を示しています。

class Temperature : System.IFormattable {
    [float]            $Degrees
    [TemperatureScale] $Scale

    Temperature() {}
    Temperature([float] $Degrees)          { $this.Degrees = $Degrees }
    Temperature([TemperatureScale] $Scale) { $this.Scale = $Scale }
    Temperature([float] $Degrees, [TemperatureScale] $Scale) {
        $this.Degrees = $Degrees
        $this.Scale = $Scale
    }

    [float] ToKelvin() {
        switch ($this.Scale) {
            Celsius { return $this.Degrees + 273.15 }
            Fahrenheit { return ($this.Degrees + 459.67) * 5 / 9 }
        }
        return $this.Degrees
    }
    [float] ToCelsius() {
        switch ($this.Scale) {
            Fahrenheit { return ($this.Degrees - 32) * 5 / 9 }
            Kelvin { return $this.Degrees - 273.15 }
        }
        return $this.Degrees
    }
    [float] ToFahrenheit() {
        switch ($this.Scale) {
            Celsius { return $this.Degrees * 9 / 5 + 32 }
            Kelvin { return $this.Degrees * 9 / 5 - 459.67 }
        }
        return $this.Degrees
    }

    [string] ToString(
        [string]$Format,
        [System.IFormatProvider]$FormatProvider
    ) {
        # If format isn't specified, use the defined scale.
        if ([string]::IsNullOrEmpty($Format)) {
            $Format = switch ($this.Scale) {
                Celsius    { 'C' }
                Fahrenheit { 'F' }
                Kelvin     { 'K' }
            }
        }
        # If format provider isn't specified, use the current culture.
        if ($null -eq $FormatProvider) {
            $FormatProvider = [cultureinfo]::CurrentCulture
        }
        # Format the temperature.
        switch ($Format) {
            'C' {
                return $this.ToCelsius().ToString('F2', $FormatProvider) + '°C'
            }
            'F' {
                return $this.ToFahrenheit().ToString('F2', $FormatProvider) + '°F'
            }
            'K' {
                return $this.ToKelvin().ToString('F2', $FormatProvider) + '°K'
            }
        }
        # If we get here, the format is invalid.
        throw [System.FormatException]::new(
            "Unknown format: '$Format'. Valid Formats are 'C', 'F', and 'K'"
        )
    }

    [string] ToString([string]$Format) {
        return $this.ToString($Format, $null)
    }

    [string] ToString() {
        return $this.ToString($null, $null)
    }
}

enum TemperatureScale {
    Celsius    = 0
    Fahrenheit = 1
    Kelvin     = 2
}

メソッドのオーバーロードの出力を次のブロックに示します。

$Temp = [Temperature]::new()
"The temperature is $Temp"
$Temp.ToString()
$Temp.ToString('K')
$Temp.ToString('F', $null)
The temperature is 0.00°C

0.00°C

273.15°K

32.00°F

IEquatable の実装

読みやすくするために Temperature クラスを書式設定できるようになったので、ユーザーはクラスの 2 つのインスタンスが等しいかどうかを確認できる必要があります。 このテストをサポートするには、System.IEquatable インターフェイスを実装する必要があります。

インターフェイスを実装するには、クラス System.IEquatable から継承し、Equals() インスタンス メソッドを定義する必要があります。 Equals() メソッドには、次のシグネチャが必要です。

[bool] Equals([Object]$Other) {
    # Implementation
}

インターフェイスに必要な署名は、リファレンス ドキュメントに記載されています。

Temperatureの場合、クラスはクラスの 2 つのインスタンスの比較のみをサポートする必要があります。 $nullを含む他の値または型の場合は、$falseを返す必要があります。 2つの温度を比較する場合、温度は異なるスケールでも同等になる可能性があるため、メソッドは両方の値をケルビンに変換する必要があります。

[bool] Equals([Object]$Other) {
    # If the other object is null, we can't compare it.
    if ($null -eq $Other) {
        return $false
    }

    # If the other object isn't a temperature, we can't compare it.
    $OtherTemperature = $Other -as [Temperature]
    if ($null -eq $OtherTemperature) {
        return $false
    }

    # Compare the temperatures as Kelvin.
    return $this.ToKelvin() -eq $OtherTemperature.ToKelvin()
}

インターフェイス メソッドを実装すると、Temperature の更新された定義は次のようになります。

class Temperature : System.IFormattable, System.IEquatable[Object] {
    [float]            $Degrees
    [TemperatureScale] $Scale

    Temperature() {}
    Temperature([float] $Degrees)          { $this.Degrees = $Degrees }
    Temperature([TemperatureScale] $Scale) { $this.Scale = $Scale }
    Temperature([float] $Degrees, [TemperatureScale] $Scale) {
        $this.Degrees = $Degrees
        $this.Scale = $Scale
    }

    [float] ToKelvin() {
        switch ($this.Scale) {
            Celsius { return $this.Degrees + 273.15 }
            Fahrenheit { return ($this.Degrees + 459.67) * 5 / 9 }
        }
        return $this.Degrees
    }
    [float] ToCelsius() {
        switch ($this.Scale) {
            Fahrenheit { return ($this.Degrees - 32) * 5 / 9 }
            Kelvin { return $this.Degrees - 273.15 }
        }
        return $this.Degrees
    }
    [float] ToFahrenheit() {
        switch ($this.Scale) {
            Celsius { return $this.Degrees * 9 / 5 + 32 }
            Kelvin { return $this.Degrees * 9 / 5 - 459.67 }
        }
        return $this.Degrees
    }

    [string] ToString(
        [string]$Format,
        [System.IFormatProvider]$FormatProvider
    ) {
        # If format isn't specified, use the defined scale.
        if ([string]::IsNullOrEmpty($Format)) {
            $Format = switch ($this.Scale) {
                Celsius    { 'C' }
                Fahrenheit { 'F' }
                Kelvin     { 'K' }
            }
        }
        # If format provider isn't specified, use the current culture.
        if ($null -eq $FormatProvider) {
            $FormatProvider = [cultureinfo]::CurrentCulture
        }
        # Format the temperature.
        switch ($Format) {
            'C' {
                return $this.ToCelsius().ToString('F2', $FormatProvider) + '°C'
            }
            'F' {
                return $this.ToFahrenheit().ToString('F2', $FormatProvider) + '°F'
            }
            'K' {
                return $this.ToKelvin().ToString('F2', $FormatProvider) + '°K'
            }
        }
        # If we get here, the format is invalid.
        throw [System.FormatException]::new(
            "Unknown format: '$Format'. Valid Formats are 'C', 'F', and 'K'"
        )
    }

    [string] ToString([string]$Format) {
        return $this.ToString($Format, $null)
    }

    [string] ToString() {
        return $this.ToString($null, $null)
    }

    [bool] Equals([Object]$Other) {
        # If the other object is null, we can't compare it.
        if ($null -eq $Other) {
            return $false
        }

        # If the other object isn't a temperature, we can't compare it.
        $OtherTemperature = $Other -as [Temperature]
        if ($null -eq $OtherTemperature) {
            return $false
        }

        # Compare the temperatures as Kelvin.
        return $this.ToKelvin() -eq $OtherTemperature.ToKelvin()
    }
}

enum TemperatureScale {
    Celsius    = 0
    Fahrenheit = 1
    Kelvin     = 2
}

次のブロックは、更新されたクラスの動作を示しています。

$Celsius    = [Temperature]::new()
$Fahrenheit = [Temperature]::new(32, 'Fahrenheit')
$Kelvin     = [Temperature]::new([TemperatureScale]::Kelvin)

@"
Temperatures are: $Celsius, $Fahrenheit, $Kelvin
`$Celsius.Equals(`$Fahrenheit) = $($Celsius.Equals($Fahrenheit))
`$Celsius -eq `$Fahrenheit     = $($Celsius -eq $Fahrenheit)
`$Celsius -ne `$Kelvin         = $($Celsius -ne $Kelvin)
"@
Temperatures are: 0.00°C, 32.00°F, 0.00°K

$Celsius.Equals($Fahrenheit) = True
$Celsius -eq $Fahrenheit     = True
$Celsius -ne $Kelvin         = True

IComparable の実装

Temperature クラスに実装する最後のインターフェイスは、System.IComparableです。 クラスがこのインターフェイスを実装すると、ユーザーは -lt-le-gt、および -ge 演算子を使用して、クラスのインスタンスを比較できます。

インターフェイスを実装するには、クラス System.IComparable から継承し、Equals() インスタンス メソッドを定義する必要があります。 Equals() メソッドには、次のシグネチャが必要です。

[int] CompareTo([Object]$Other) {
    # Implementation
}

インターフェイスに必要な署名は、リファレンス ドキュメントに記載されています。

Temperatureの場合、クラスはクラスの 2 つのインスタンスの比較のみをサポートする必要があります。 Degrees プロパティの基になる型は、別のスケールに変換された場合でも浮動小数点数であるため、メソッドは実際の比較に基になる型に依存できます。

[int] CompareTo([Object]$Other) {
    # If the other object's null, consider this instance "greater than" it
    if ($null -eq $Other) {
        return 1
    }
    # If the other object isn't a temperature, we can't compare it.
    $OtherTemperature = $Other -as [Temperature]
    if ($null -eq $OtherTemperature) {
        throw [System.ArgumentException]::new(
            "Object must be of type 'Temperature'."
        )
    }
    # Compare the temperatures as Kelvin.
    return $this.ToKelvin().CompareTo($OtherTemperature.ToKelvin())
}

Temperature クラスの最終的な定義は次のとおりです。

class Temperature : System.IFormattable,
                    System.IComparable,
                    System.IEquatable[Object] {
    # Instance properties
    [float]            $Degrees
    [TemperatureScale] $Scale

    # Constructors
    Temperature() {}
    Temperature([float] $Degrees)          { $this.Degrees = $Degrees }
    Temperature([TemperatureScale] $Scale) { $this.Scale = $Scale }
    Temperature([float] $Degrees, [TemperatureScale] $Scale) {
        $this.Degrees = $Degrees
        $this.Scale = $Scale
    }

    [float] ToKelvin() {
        switch ($this.Scale) {
            Celsius { return $this.Degrees + 273.15 }
            Fahrenheit { return ($this.Degrees + 459.67) * 5 / 9 }
        }
        return $this.Degrees
    }
    [float] ToCelsius() {
        switch ($this.Scale) {
            Fahrenheit { return ($this.Degrees - 32) * 5 / 9 }
            Kelvin { return $this.Degrees - 273.15 }
        }
        return $this.Degrees
    }
    [float] ToFahrenheit() {
        switch ($this.Scale) {
            Celsius { return $this.Degrees * 9 / 5 + 32 }
            Kelvin { return $this.Degrees * 9 / 5 - 459.67 }
        }
        return $this.Degrees
    }

    [string] ToString(
        [string]$Format,
        [System.IFormatProvider]$FormatProvider
    ) {
        # If format isn't specified, use the defined scale.
        if ([string]::IsNullOrEmpty($Format)) {
            $Format = switch ($this.Scale) {
                Celsius    { 'C' }
                Fahrenheit { 'F' }
                Kelvin     { 'K' }
            }
        }
        # If format provider isn't specified, use the current culture.
        if ($null -eq $FormatProvider) {
            $FormatProvider = [cultureinfo]::CurrentCulture
        }
        # Format the temperature.
        switch ($Format) {
            'C' {
                return $this.ToCelsius().ToString('F2', $FormatProvider) + '°C'
            }
            'F' {
                return $this.ToFahrenheit().ToString('F2', $FormatProvider) + '°F'
            }
            'K' {
                return $this.ToKelvin().ToString('F2', $FormatProvider) + '°K'
            }
        }
        # If we get here, the format is invalid.
        throw [System.FormatException]::new(
            "Unknown format: '$Format'. Valid Formats are 'C', 'F', and 'K'"
        )
    }

    [string] ToString([string]$Format) {
        return $this.ToString($Format, $null)
    }

    [string] ToString() {
        return $this.ToString($null, $null)
    }

    [bool] Equals([Object]$Other) {
        # If the other object is null, we can't compare it.
        if ($null -eq $Other) {
            return $false
        }

        # If the other object isn't a temperature, we can't compare it.
        $OtherTemperature = $Other -as [Temperature]
        if ($null -eq $OtherTemperature) {
            return $false
        }

        # Compare the temperatures as Kelvin.
        return $this.ToKelvin() -eq $OtherTemperature.ToKelvin()
    }
    [int] CompareTo([Object]$Other) {
        # If the other object's null, consider this instance "greater than" it
        if ($null -eq $Other) {
            return 1
        }
        # If the other object isn't a temperature, we can't compare it.
        $OtherTemperature = $Other -as [Temperature]
        if ($null -eq $OtherTemperature) {
            throw [System.ArgumentException]::new(
                "Object must be of type 'Temperature'."
            )
        }
        # Compare the temperatures as Kelvin.
        return $this.ToKelvin().CompareTo($OtherTemperature.ToKelvin())
    }
}

enum TemperatureScale {
    Celsius    = 0
    Fahrenheit = 1
    Kelvin     = 2
}

完全な定義を使用すると、ユーザーは PowerShell でクラスのインスタンスを任意の組み込み型と同様に書式設定および比較できます。

$Celsius    = [Temperature]::new()
$Fahrenheit = [Temperature]::new(32, 'Fahrenheit')
$Kelvin     = [Temperature]::new([TemperatureScale]::Kelvin)

@"
Temperatures are: $Celsius, $Fahrenheit, $Kelvin
`$Celsius.Equals(`$Fahrenheit)    = $($Celsius.Equals($Fahrenheit))
`$Celsius.Equals(`$Kelvin)        = $($Celsius.Equals($Kelvin))
`$Celsius.CompareTo(`$Fahrenheit) = $($Celsius.CompareTo($Fahrenheit))
`$Celsius.CompareTo(`$Kelvin)     = $($Celsius.CompareTo($Kelvin))
`$Celsius -lt `$Fahrenheit        = $($Celsius -lt $Fahrenheit)
`$Celsius -le `$Fahrenheit        = $($Celsius -le $Fahrenheit)
`$Celsius -eq `$Fahrenheit        = $($Celsius -eq $Fahrenheit)
`$Celsius -gt `$Kelvin            = $($Celsius -gt $Kelvin)
"@
Temperatures are: 0.00°C, 32.00°F, 0.00°K
$Celsius.Equals($Fahrenheit)    = True
$Celsius.Equals($Kelvin)        = False
$Celsius.CompareTo($Fahrenheit) = 0
$Celsius.CompareTo($Kelvin)     = 1
$Celsius -lt $Fahrenheit        = False
$Celsius -le $Fahrenheit        = True
$Celsius -eq $Fahrenheit        = True
$Celsius -gt $Kelvin            = True

例 3 - ジェネリック 基底クラスからの継承

この例では、型パラメーターが解析時に既に定義されている限り、ジェネリック型から派生させる方法を示します。

型パラメーターとして組み込みクラスを使用する

次のコード ブロックを実行します。 これは、型パラメーターが解析時に既に定義されている限り、新しいクラスがジェネリック型から継承する方法を示しています。

class ExampleStringList : System.Collections.Generic.List[string] {}

$List = [ExampleStringList]::new()
$List.AddRange([string[]]@('a','b','c'))
$List.GetType() | Format-List -Property Name, BaseType
$List
Name     : ExampleStringList
BaseType : System.Collections.Generic.List`1[System.String]

a
b
c

カスタム クラスを型パラメーターとして使用する

次のコード ブロックでは、最初に、単一のインスタンス プロパティと メソッドを使用して、ExampleItem新しいクラスを定義します。 次に、System.Collections.Generic.List 基底クラスから継承する ExampleItemList クラスを、型パラメーターとして ExampleItem 定義します。

コード ブロック全体をコピーし、1 つのステートメントとして実行します。

class ExampleItem {
    [string] $Name
    [string] ToString() { return $this.Name }
}
class ExampleItemList : System.Collections.Generic.List[ExampleItem] {}
ParentContainsErrorRecordException: An error occurred while creating the pipeline.

PowerShell で ExampleItem クラスがまだランタイムに読み込まれていないため、コード ブロック全体を実行するとエラーが発生します。 System.Collections.Generic.List 基底クラスの型パラメーターとしてクラス名を使用することはできません。

次のコード ブロックを定義された順序で実行します。

class ExampleItem {
    [string] $Name
    [string] ToString() { return $this.Name }
}
class ExampleItemList : System.Collections.Generic.List[ExampleItem] {}

今回は、PowerShell でエラーが発生することはありません。 これで、両方のクラスが定義されました。 次のコード ブロックを実行して、新しいクラスの動作を表示します。

$List = [ExampleItemList]::new()
$List.AddRange([ExampleItem[]]@(
    [ExampleItem]@{ Name = 'Foo' }
    [ExampleItem]@{ Name = 'Bar' }
    [ExampleItem]@{ Name = 'Baz' }
))
$List.GetType() | Format-List -Property Name, BaseType
$List
Name     : ExampleItemList
BaseType : System.Collections.Generic.List`1[ExampleItem]

Name
----
Foo
Bar
Baz

モジュール内のカスタム型パラメーターを使用したジェネリックの派生

次のコード ブロックは、型パラメーターにカスタム型を使用するジェネリック 基底クラスから継承するクラスを定義する方法を示しています。

次のコード ブロックを GenericExample.psd1として保存します。

@{
    RootModule        = 'GenericExample.psm1'
    ModuleVersion     = '0.1.0'
    GUID              = '2779fa60-0b3b-4236-b592-9060c0661ac2'
}

次のコード ブロックを GenericExample.InventoryItem.psm1として保存します。

class InventoryItem {
    [string] $Name
    [int]    $Count

    InventoryItem() {}
    InventoryItem([string]$Name) {
        $this.Name = $Name
    }
    InventoryItem([string]$Name, [int]$Count) {
        $this.Name  = $Name
        $this.Count = $Count
    }

    [string] ToString() {
        return "$($this.Name) ($($this.Count))"
    }
}

次のコード ブロックを GenericExample.psm1として保存します。

using namespace System.Collections.Generic
using module ./GenericExample.InventoryItem.psm1

class Inventory : List[InventoryItem] {}

# Define the types to export with type accelerators.
$ExportableTypes =@(
    [InventoryItem]
    [Inventory]
)
# Get the internal TypeAccelerators class to use its static methods.
$TypeAcceleratorsClass = [psobject].Assembly.GetType(
    'System.Management.Automation.TypeAccelerators'
)
# Ensure none of the types would clobber an existing type accelerator.
# If a type accelerator with the same name exists, throw an exception.
$ExistingTypeAccelerators = $TypeAcceleratorsClass::Get
foreach ($Type in $ExportableTypes) {
    if ($Type.FullName -in $ExistingTypeAccelerators.Keys) {
        $Message = @(
            "Unable to register type accelerator '$($Type.FullName)'"
            'Accelerator already exists.'
        ) -join ' - '

        throw [System.Management.Automation.ErrorRecord]::new(
            [System.InvalidOperationException]::new($Message),
            'TypeAcceleratorAlreadyExists',
            [System.Management.Automation.ErrorCategory]::InvalidOperation,
            $Type.FullName
        )
    }
}
# Add type accelerators for every exportable type.
foreach ($Type in $ExportableTypes) {
    $TypeAcceleratorsClass::Add($Type.FullName, $Type)
}
# Remove type accelerators when the module is removed.
$MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = {
    foreach($Type in $ExportableTypes) {
        $TypeAcceleratorsClass::Remove($Type.FullName)
    }
}.GetNewClosure()

ヒント

ルート モジュールは、PowerShell の型アクセラレータにカスタム型を追加します。 このパターンにより、モジュール ユーザーは、最初に using module ステートメントを使用しなくても、カスタム型の IntelliSense とオートコンプリートにすぐにアクセスできます。

このパターンの詳細については、about_Classesの「型アクセラレータを使用したエクスポート」セクションを参照してください。

モジュールをインポートし、出力を確認します。

Import-Module ./GenericExample.psd1

$Inventory = [Inventory]::new()
$Inventory.GetType() | Format-List -Property Name, BaseType

$Inventory.Add([InventoryItem]::new('Bucket', 2))
$Inventory.Add([InventoryItem]::new('Mop'))
$Inventory.Add([InventoryItem]@{ Name = 'Broom' ; Count = 4 })
$Inventory
Name     : Inventory
BaseType : System.Collections.Generic.List`1[InventoryItem]

Name   Count
----   -----
Bucket     2
Mop        0
Broom      4

InventoryItem クラスが Inventory クラスとは異なるモジュール ファイルで定義されているため、モジュールはエラーなしで読み込まれます。 どちらのクラスもモジュール ユーザーが使用できます。

基底クラスの継承

クラスが基底クラスから継承されると、基底クラスのプロパティとメソッドが継承されます。 基底クラスのコンストラクターは直接継承されませんが、呼び出すことができます。

PowerShell ではなく .NET で基本クラスが定義されている場合は、次の点に注意してください。

  • PowerShell クラスは、シールクラスから継承できません。
  • ジェネリック 基底クラスから継承する場合、ジェネリック クラスの型パラメーターを派生クラスにすることはできません。 派生クラスを型パラメーターとして使用すると、解析エラーが発生します。

派生クラスの継承とオーバーライドのしくみについては、例 1参照してください。

派生クラスコンストラクター

派生クラスは、基底クラスのコンストラクターを直接継承しません。 基底クラスが既定のコンストラクターを定義し、派生クラスでコンストラクターが定義されていない場合、派生クラスの新しいインスタンスは基底クラスの既定のコンストラクターを使用します。 基底クラスで既定のコンストラクターが定義されていない場合、派生クラスは少なくとも 1 つのコンストラクターを明示的に定義する必要があります。

派生クラスコンストラクターは、base キーワードを使用して基底クラスからコンストラクターを呼び出すことができます。 派生クラスが基底クラスからコンストラクターを明示的に呼び出さない場合は、代わりに基底クラスの既定のコンストラクターが呼び出されます。

既定以外の基本コンストラクターを呼び出すには、コンストラクター パラメーターの後と body ブロックの前に : base(<parameters>) を追加します。

class <derived-class> : <base-class> {
    <derived-class>(<derived-parameters>) : <base-class>(<base-parameters>) {
        # initialization code
    }
}

基底クラスのコンストラクターを呼び出すコンストラクターを定義する場合、パラメーターには次のいずれかの項目を指定できます。

  • 派生クラス コンストラクターの任意のパラメーターの変数。
  • 任意の静的値。
  • パラメーター型の値に評価されるすべての式。

例 1 クラスは、派生クラスで基底クラスコンストラクターを使用する方法を示しています。

派生クラス メソッド

基底クラスから派生したクラスは、基底クラスとそのオーバーロードのメソッドを継承します。 基底クラスで定義されているメソッド オーバーロード (非表示のメソッドを含む) は、派生クラスで使用できます。

派生クラスは、クラス定義で再定義することで、継承されたメソッドオーバーロードをオーバーライドできます。 オーバーロードをオーバーライドするには、パラメーターの型が基底クラスと同じである必要があります。 オーバーロードの出力の種類は異なる場合があります。

コンストラクターとは異なり、メソッドは : base(<parameters>) 構文を使用してメソッドの基底クラスオーバーロードを呼び出すことはできません。 派生クラスで再定義されたオーバーロードは、基底クラスによって定義されたオーバーロードを完全に置き換えます。 インスタンスの基底クラス メソッドを呼び出すには、メソッドを呼び出す前に、インスタンス変数 ($this) を基底クラスにキャストします。

次のスニペットは、派生クラスが基底クラス メソッドを呼び出す方法を示しています。

class BaseClass {
    [bool] IsTrue() { return $true }
}
class DerivedClass : BaseClass {
    [bool] IsTrue()     { return $false }
    [bool] BaseIsTrue() { return ([BaseClass]$this).IsTrue() }
}

@"
[BaseClass]::new().IsTrue()        = $([BaseClass]::new().IsTrue())
[DerivedClass]::new().IsTrue()     = $([DerivedClass]::new().IsTrue())
[DerivedClass]::new().BaseIsTrue() = $([DerivedClass]::new().BaseIsTrue())
"@
[BaseClass]::new().IsTrue()        = True
[DerivedClass]::new().IsTrue()     = False
[DerivedClass]::new().BaseIsTrue() = True

派生クラスが継承されたメソッドをオーバーライドする方法を示す拡張サンプルについては、例 1 クラスを参照してください。

派生クラスのプロパティ

基底クラスから派生したクラスは、基底クラスのプロパティを継承します。 基底クラスで定義されているすべてのプロパティ (非表示のプロパティを含む) は、派生クラスで使用できます。

派生クラスは、継承されたプロパティをクラス定義で再定義することでオーバーライドできます。 派生クラスのプロパティは、再定義された型と既定値 (存在する場合) を使用します。 継承されたプロパティで既定値が定義されていて、再定義されたプロパティが定義されていない場合、継承されたプロパティには既定値はありません。

派生クラスが静的プロパティをオーバーライドしない場合、派生クラスを介して静的プロパティにアクセスすると、基底クラスの静的プロパティにアクセスします。 派生クラスを使用してプロパティ値を変更すると、基底クラスの値が変更されます。 静的プロパティをオーバーライドしないその他の派生クラスも、基底クラスのプロパティの値を使用します。 プロパティをオーバーライドしないクラスで継承された静的プロパティの値を更新すると、同じ基底クラスから派生したクラスに対して意図しない効果が発生する可能性があります。

例 1 、基底クラスのプロパティを継承、拡張、およびオーバーライドする派生クラスを示します。

ジェネリックからの派生

クラスがジェネリックから派生する場合、PowerShell が派生クラスを解析する前に、型パラメーターが既に定義されている必要があります。 ジェネリックの型パラメーターが、同じファイルまたはコード ブロックで定義されている PowerShell クラスまたは列挙型である場合、PowerShell はエラーを発生させます。

カスタム型を型パラメーターとして持つジェネリック 基底クラスからクラスを派生するには、別のファイルまたはモジュールで型パラメーターのクラスまたは列挙型を定義し、using module ステートメントを使用して型定義を読み込みます。

ジェネリック 基底クラスから継承する方法を示す例については、例 3を参照してください。

継承する便利なクラス

PowerShell モジュールを作成するときに継承するのに役立つクラスがいくつかあります。 このセクションでは、いくつかの基本クラスと、そこから派生したクラスを使用できる対象を示します。

  • System.Attribute - 変数、パラメーター、クラスおよび列挙型の定義に使用できる属性を定義するクラスを派生させます。
  • System.Management.Automation.ArgumentTransformationAttribute - 変数またはパラメーターの入力を特定のデータ型に変換する処理を行うクラスを派生させます。
  • System.Management.Automation.ValidateArgumentsAttribute - 変数、パラメーター、およびクラス プロパティにカスタム検証を適用するクラスを派生させます。
  • System.Collections.Generic.List - クラスを派生させ、特定のデータ型のリストの作成と管理を容易にします。
  • System.Exception - カスタム エラーを定義するクラスを派生させます。

インターフェイスの実装

インターフェイスを実装する PowerShell クラスは、そのインターフェイスのすべてのメンバーを実装する必要があります。 実装インターフェイスのメンバーを省略すると、スクリプトで解析時エラーが発生します。

手記

PowerShell では、PowerShell スクリプトでの新しいインターフェイスの宣言はサポートされていません。 代わりに、インターフェイスを .NET コードで宣言し、Add-Type コマンドレットまたは using assembly ステートメントを使用してセッションに追加する必要があります。

クラスがインターフェイスを実装する場合は、そのインターフェイスを実装する他のクラスと同様に使用できます。 一部のコマンドと操作では、サポートされている型が特定のインターフェイスを実装するクラスに制限されます。

インターフェイスの実装例を確認するには、例 2を参照してください。

実装する便利なインターフェイス

PowerShell モジュールを作成するときに継承するのに役立つインターフェイス クラスがいくつかあります。 このセクションでは、いくつかの基本クラスと、そこから派生したクラスを使用できる対象を示します。

  • System.IEquatable - このインターフェイスを使用すると、ユーザーはクラスの 2 つのインスタンスを比較できます。 クラスがこのインターフェイスを実装していない場合、PowerShell は参照等価性を使用して 2 つのインスタンス間の等価性をチェックします。 つまり、2 つのインスタンスのプロパティ値が同じ場合でも、クラスのインスタンスはそれ自体と等しくなります。
  • System.IComparable - このインターフェイスを使用すると、ユーザーはクラスのインスタンスを、-le-lt-ge、および -gt 比較演算子と比較できます。 クラスがこのインターフェイスを実装していない場合、これらの演算子はエラーを発生させます。
  • System.IFormattable - このインターフェイスを使用すると、ユーザーはクラスのインスタンスを別の文字列に書式設定できます。 これは、予算項目、文献目録、温度など、複数の標準文字列表現を持つクラスに役立ちます。
  • System.IConvertible - このインターフェイスを使用すると、ユーザーはクラスのインスタンスを他のランタイム型に変換できます。 これは、基になる数値を持つクラスや、1 つに変換できるクラスに役立ちます。

制限事項

  • PowerShell では、スクリプト コードでのインターフェイスの定義はサポートされていません。

    回避策: C# でインターフェイスを定義し、インターフェイスを定義するアセンブリを参照します。

  • PowerShell クラスは、1 つの基本クラスからのみ継承できます。

    回避策: クラスの継承は推移的です。 派生クラスは、別の派生クラスから継承して、基底クラスのプロパティとメソッドを取得できます。

  • ジェネリック クラスまたはインターフェイスから継承する場合は、ジェネリックの型パラメーターが既に定義されている必要があります。 クラス自体をクラスまたはインターフェイスの型パラメーターとして定義することはできません。

    回避策: ジェネリック 基底クラスまたはインターフェイスから派生するには、別の .psm1 ファイルでカスタム型を定義し、using module ステートメントを使用して型を読み込みます。 ジェネリックから継承するときに、カスタム型が型パラメーターとしてそれ自体を使用する回避策はありません。

こちらも参照ください