次の方法で共有


about_Classes_Inheritance

簡単な説明

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

長い説明

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

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

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

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

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

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

一部の PowerShell 演算子は、特定のインターフェイスを実装するクラスに依存します。 たとえば、クラスが -eqSystem.IEquatable インターフェイスを実装しない限り、演算子は参照の等価性のみをチェックします。 、-lt-geおよび -gt 演算子は-leSystem.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 を基本クラスとして定義します。 これには、 List と Artists という 2 つの静的プロパティ があります。 次に、静的な List プロパティに作品を追加する静的RegisterWork()メソッドを定義し、アーティストを Artists プロパティに追加し、リスト内の新しいエントリごとにメッセージを書き込みます。

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

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

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

次のコード ブロックはレジストリをクリアし、Album オブジェクトの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 クラスを定義します。 新しいクラスは、既定値Unknownが の Medium インスタンス プロパティを定義することで、基底クラスを拡張します。

派生 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"
    }
}

次のコード ブロックは、派生 した 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() 静的 List プロパティのエントリで メソッドを呼び出します。

[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 インスタンスは、文字列内の名前とアーティストのみを返します。 また、そのクラスが メソッドをオーバーロデするため、Illustration インスタンスにはかっこ内の中も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 の場合、クラスは、インスタンスを摂氏で返し、華氏で返しKFケルビンで返すという 3 つの形式Cをサポートする必要があります。 その他の形式の場合、メソッドは 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'"
    )
}

この実装では、数値度値自体を書式設定する場合、メソッドは既定で format のインスタンス スケールと現在のカルチャに設定されます。 インスタンス メソッドを 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 です。 クラスがこのインターフェイスを実装すると、ユーザーは 、-le-gt、、および -ge 演算子を-lt使用して、 クラスのインスタンスを比較できます。

インターフェイスを実装するには、クラスが 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 - ジェネリック 基底クラスからの継承

この例では、 System.Collections.Generic.List などのジェネリック クラスから派生する方法を示します。

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

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

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

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

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

コード ブロック全体をコピーし、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 つのコンストラクターを明示的に定義する必要があります。

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

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

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

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

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

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

派生クラス のメソッド

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

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

コンストラクターとは異なり、メソッドは 構文を : 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

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

派生クラスのプロパティ

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

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

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

例 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 - このインターフェイスを使用すると、ユーザーは クラスのインスタンスを 、-lt-ge、および 比較演算子と-le-gt比較できます。 クラスがこのインターフェイスを実装していない場合、これらの演算子はエラーを発生させます。
  • System.IFormattable - このインターフェイスを使用すると、ユーザーは クラスのインスタンスを別の文字列に書式設定できます。 これは、予算項目、文献目録、気温など、複数の標準文字列表現を持つクラスに役立ちます。
  • System.IConvertible - このインターフェイスを使用すると、ユーザーは クラスのインスタンスを他のランタイム型に変換できます。 これは、基になる数値を持つクラス、または 1 つに変換できるクラスに役立ちます。

制限事項

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

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

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

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

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

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

参照