about_Classes_Inheritance

간단한 설명

다른 형식을 확장하는 클래스를 정의하는 방법을 설명합니다.

자세한 설명

PowerShell 클래스는 상속을 지원하므로 부모 클래스의 동작을 재사용(상속), 확장 또는 수정하는 자식 클래스를 정의할 수 있습니다. 멤버가 상속되는 클래스를 기본 클래스라고 합니다. 기본 클래스의 멤버를 상속하는 클래스를 파생 클래스라고 합니다.

PowerShell은 단일 상속만 지원합니다. 클래스는 단일 클래스에서만 상속할 수 있습니다. 그러나 상속은 전이적이므로 형식 집합에 대해 상속 계층을 정의할 수 있습니다. 즉, D 형식은 기본 클래스 형식 A에서 상속되는 B 형식에서 상속되는 C 형식에서 상속할 수 있습니다. 상속은 전이적이므로 A 형식의 멤버를 D 형식에 사용할 수 있습니다.

파생 클래스는 기본 클래스의 모든 멤버를 상속하지 않습니다. 다음 멤버는 상속되지 않습니다.

  • 정적 생성자: 클래스의 정적 데이터를 초기화합니다.
  • 인스턴스 생성자: 클래스의 새 인스턴스를 만들기 위해 호출합니다. 각 클래스는 자체 생성자를 정의해야 합니다.

기존 클래스에서 파생되는 새 클래스를 만들어 클래스를 확장할 수 있습니다. 파생 클래스는 기본 클래스의 속성과 메서드를 상속합니다. 필요에 따라 기본 클래스 멤버를 추가하거나 재정의할 수 있습니다.

클래스는 계약을 정의하는 인터페이스에서 상속할 수도 있습니다. 인터페이스에서 상속되는 클래스는 해당 계약을 구현해야 합니다. 이 경우 클래스는 해당 인터페이스를 구현하는 다른 클래스와 마찬가지로 사용할 수 있습니다. 클래스가 인터페이스에서 상속되지만 인터페이스를 구현하지 않는 경우 PowerShell은 클래스에 대한 구문 분석 오류를 발생합니다.

일부 PowerShell 연산자는 특정 인터페이스를 구현하는 클래스에 따라 달라집니다. 예를 들어 클래스가 -eq System.IEquatable 인터페이스를 구현하지 않는 한 연산자는 참조 같음의 검사. , 및 -ge연산자는 -leSystem.IComparable 인터페이스를 구현하는 클래스에서만 작동합니다.-gt-lt

파생 클래스는 : 구문을 사용하여 기본 클래스를 확장하거나 인터페이스를 구현합니다. 파생 클래스는 항상 클래스 선언에서 맨 왼쪽에 있어야 합니다.

이 예제에서는 기본 PowerShell 클래스 상속 구문을 보여 줍니다.

Class Derived : Base {...}

이 예제에서는 기본 클래스 다음에 오는 인터페이스 선언을 사용하여 상속을 보여 줍니다.

Class Derived : Base, Interface {...}

구문

클래스 상속은 다음 구문을 사용합니다.

한 줄 구문

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의 두 가지 정적 속성이 있습니다. 다음으로 정적 List 속성에 작품을 추가하고 Artists 속성에 아티스트를 추가하는 정적 RegisterWork()메서드를 정의하여 목록의 각 새 항목에 대한 메시지를 작성합니다.

이 클래스는 게시된 작업을 설명하는 세 가지 인스턴스 속성을 정의합니다. 마지막으로, 인스턴스 메서드와 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 또는 생성자에 대한 값을 정의하지 않았더라도 속성은 기본 클래스의 기본 생성자에 의해 정의되었습니다.

자세한 메시지에서 메서드에 대한 RegisterWork() 두 번째 호출은 작품과 아티스트가 이미 등록되어 있음을 보고합니다. 파생된 Album 클래스에 RegisterWork() 대한 첫 번째 호출이었음에도 불구하고 기본 PublishedWork 클래스에서 상속된 정적 메서드를 사용했습니다. 이 메서드는 파생 클래스가 재정의하지 않은 기본 클래스의 정적 ListArtist 속성을 업데이트했습니다.

다음 코드 블록은 레지스트리를 지우고 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 클래스는 정의가 직접 선언합니다.
  • 기본값Illustrations.로 설정하여 Category 인스턴스 속성을 재정의합니다.
  • 인스턴스 메서드를 ToString() 재정의하여 일러스트레이션의 문자열 표현에 만든 매체가 포함되도록 합니다.

또한 클래스는 먼저 기본 클래스 메서드를 호출한 다음 파생 클래스 RegisterWork() 의 재정의된 Artists 정적 속성에 아티스트를 추가하는 정적 RegisterIllustration() 메서드를 정의합니다.

마지막으로 클래스는 세 가지 생성자를 모두 재정의합니다.

  1. 기본 생성자는 그림을 만들었다는 자세한 메시지를 제외하고 비어 있습니다.
  2. 다음 생성자는 그림을 만든 이름과 아티스트에 대해 두 개의 문자열 값을 사용합니다. NameArtist 속성을 설정하는 논리를 구현하는 대신 생성자는 기본 클래스에서 적절한 생성자를 호출합니다.
  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

인스턴스를 만드는 자세한 내용은 다음을 보여 줍니다.

  • 첫 번째 인스턴스를 만들 때 기본 클래스 기본 생성자는 파생 클래스 기본 생성자 전에 호출되었습니다.
  • 두 번째 인스턴스를 만들 때 파생 클래스 생성자 이전에 기본 클래스에 대해 명시적으로 상속된 생성자가 호출되었습니다.
  • 세 번째 인스턴스를 만들 때 파생 클래스 생성자 전에 기본 클래스 기본 생성자가 호출되었습니다.

메서드의 RegisterWork() 자세한 내용은 작품과 예술가가 이미 등록되었음을 나타냅니다. 메서드가 RegisterIllustration() 내부적으로 메서드를 호출했기 RegisterWork() 때문입니다.

그러나 기본 클래스와 파생 클래스의 정적 Artist 속성 값을 비교할 때 값은 다릅니다. 파생 클래스의 Artists 속성에는 앨범 아티스트가 아닌 일러스트레이터만 포함됩니다. 파생 클래스에서 Artist 속성을 다시 정의하면 클래스가 기본 클래스에서 정적 속성을 반환하지 못하게 됩니다.

최종 코드 블록은 기본 클래스의 정적 List 속성 항목에서 메서드를 호출 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)

앨범 인스턴스는 문자열에 이름과 아티스트만 반환합니다. 일러스트레이션 인스턴스에는 해당 클래스가 메서드를 오버로드했기 때문에 괄호 안에 매체도 포함되었습니다ToString().

예제 2 - 인터페이스 구현

다음 예제에서는 클래스가 하나 이상의 인터페이스를 구현하는 방법을 보여 줍니다. 이 예제에서는 더 많은 작업 및 동작을 지원하도록 온도 클래스의 정의를 확장합니다.

초기 클래스 정의

인터페이스를 구현하기 전에 Temperature 클래스는 Degrees 및 Scale이라는 두 가지 속성으로 정의됩니다. 특정 눈금의 각도로 인스턴스를 반환하기 위한 생성자와 세 가지 인스턴스 메서드를 정의합니다.

클래스는 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.

출력은 온도 인스턴스를 보여 줍니다.

  • 문자열로 올바르게 표시되지 않습니다.
  • 동등성을 위해 제대로 검사 수 없습니다.
  • 비교할 수 없습니다.

이러한 세 가지 문제는 클래스에 대한 인터페이스를 구현하여 해결할 수 있습니다.

IFormattable 구현

온도 클래스에 대해 구현할 첫 번째 인터페이스는 System.IFormattable입니다. 이 인터페이스를 사용하면 클래스 인스턴스의 서식을 다른 문자열로 지정할 수 있습니다. 인터페이스를 구현하려면 클래스가 System.IFormattable에서 상속되고 인스턴스 메서드를 ToString() 정의해야 합니다.

인스턴스 메서드에는 ToString() 다음 서명이 있어야 합니다.

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

인터페이스에 필요한 서명은 참조 설명서나열됩니다.

온도의 경우 클래스는 인스턴스를 섭씨로 반환하고 화 F 씨로 반환하고 K 켈빈으로 반환하는 세 가지 형식 C 을 지원해야 합니다. 다른 형식의 경우 메서드는 System.FormatExceptionthrow해야 합니다.

[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)
}

다음 코드는 온도에 대한 업데이트된 정의를 보여줍니다.

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 구현

이제 온도 클래스를 가독성을 위해 형식을 지정할 수 있으므로 사용자는 클래스의 두 인스턴스가 같은지 여부를 검사 수 있어야 합니다. 이 테스트를 지원하려면 클래스가 System.IEquatable 인터페이스를 구현해야 합니다.

인터페이스를 구현하려면 클래스가 System.IEquatable에서 상속되고 인스턴스 메서드를 Equals() 정의해야 합니다. 메서드에는 Equals() 다음 서명이 있어야 합니다.

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

인터페이스에 필요한 서명은 참조 설명서나열됩니다.

Temperature의 경우 클래스는 클래스의 두 인스턴스 비교만 지원해야 합니다. 을 비롯한 $null다른 값 또는 형식의 경우 반환 $false해야 합니다. 두 온도를 비교할 때 온도가 서로 다른 경우에도 동일할 수 있으므로 두 값을 모두 Kelvin으로 변환해야 합니다.

[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()
}

인터페이스 메서드를 구현하면 온도에 대한 업데이트된 정의는 다음과 같습니다.

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 구현

온도 클래스에 대해 구현할 마지막 인터페이스는 System.IComparable입니다. 클래스가 이 인터페이스를 구현할 때 사용자는 , -le-gt-ge 연산자를 사용하여 -lt클래스의 인스턴스를 비교할 수 있습니다.

인터페이스를 구현하려면 클래스가 System.IComparable에서 상속되고 인스턴스 메서드를 Equals() 정의해야 합니다. 메서드에는 Equals() 다음 서명이 있어야 합니다.

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

인터페이스에 필요한 서명은 참조 설명서나열됩니다.

Temperature의 경우 클래스는 클래스의 두 인스턴스 비교만 지원해야 합니다. 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())
}

온도 클래스에 대한 최종 정의는 다음과 같습니다.

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() 정의합니다. 그런 다음, ExampleItem을 형식 매개 변수로 사용하여 System.Collections.Generic.List 기본 클래스에서 상속하는 ExampleItemList 클래스를 정의합니다.

전체 코드 블록을 복사하고 단일 문으로 실행합니다.

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을 참조하세요.

파생 클래스 생성자

파생 클래스는 기본 클래스의 생성자를 직접 상속하지 않습니다. 기본 클래스가 기본 생성자를 정의하고 파생 클래스가 생성자를 정의하지 않는 경우 파생 클래스의 새 인스턴스는 기본 클래스 기본 생성자를 사용합니다. 기본 클래스가 기본 생성자를 정의하지 않는 경우 파생 클래스는 하나 이상의 생성자를 명시적으로 정의해야 합니다.

파생 클래스 생성자는 키워드(keyword) 사용하여 기본 클래스에서 생성자를 호출할 base 수 있습니다. 파생 클래스가 기본 클래스에서 생성자를 명시적으로 호출하지 않으면 기본 클래스에 대한 기본 생성자를 대신 호출합니다.

기본이 아닌 기본 생성자를 호출하려면 생성자 매개 변수 뒤와 본문 블록 앞에 추가 : 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 코드에서 선언하고 cmdlet 또는 문을 사용하여 세션에 Add-Typeusing assembly 추가해야 합니다.

클래스가 인터페이스를 구현하는 경우 해당 인터페이스를 구현하는 다른 클래스와 마찬가지로 사용할 수 있습니다. 일부 명령 및 작업은 지원되는 형식을 특정 인터페이스를 구현하는 클래스로 제한합니다.

인터페이스의 샘플 구현을 검토하려면 예제 2를 참조하세요.

구현할 유용한 인터페이스

PowerShell 모듈을 작성할 때 상속하는 데 유용할 수 있는 몇 가지 인터페이스 클래스가 있습니다. 이 섹션에는 몇 가지 기본 클래스와 파생된 클래스를 사용할 수 있는 항목이 나열되어 있습니다.

  • System.IEquatable - 이 인터페이스를 사용하면 사용자가 클래스의 두 인스턴스를 비교할 수 있습니다. 클래스가 이 인터페이스를 구현하지 않으면 PowerShell은 참조 같음을 사용하여 두 인스턴스 간의 동등성을 검사. 즉, 클래스의 인스턴스는 두 인스턴스의 속성 값이 같더라도 자체만 같아집니다.
  • System.IComparable - 이 인터페이스를 사용하면 클래스-le의 인스턴스를 , -lt-ge비교 연산자와 -gt 비교할 수 있습니다. 클래스가 이 인터페이스를 구현하지 않으면 해당 연산자가 오류를 발생합니다.
  • System.IFormattable - 이 인터페이스를 사용하면 클래스 인스턴스의 서식을 다른 문자열로 지정할 수 있습니다. 예산 항목, 참고 문헌 및 온도와 같이 둘 이상의 표준 문자열 표현이 있는 클래스에 유용합니다.
  • System.IConvertible - 이 인터페이스를 사용하면 클래스의 인스턴스를 다른 런타임 형식으로 변환할 수 있습니다. 이는 기본 숫자 값이 있거나 숫자 값으로 변환할 수 있는 클래스에 유용합니다.

제한 사항

  • PowerShell은 스크립트 코드에서 인터페이스 정의를 지원하지 않습니다.

    해결 방법: C#에서 인터페이스를 정의하고 인터페이스를 정의하는 어셈블리를 참조합니다.

  • PowerShell 클래스는 하나의 기본 클래스에서만 상속할 수 있습니다.

    해결 방법: 클래스 상속은 전이적입니다. 파생 클래스는 다른 파생 클래스에서 상속하여 기본 클래스의 속성과 메서드를 가져올 수 있습니다.

  • 제네릭 클래스 또는 인터페이스에서 상속하는 경우 제네릭에 대한 형식 매개 변수가 이미 정의되어 있어야 합니다. 클래스는 클래스 또는 인터페이스에 대한 형식 매개 변수로 자신을 정의할 수 없습니다.

    해결 방법: 제네릭 기본 클래스 또는 인터페이스에서 파생하려면 다른 .psm1 파일에서 사용자 지정 형식을 정의하고 문을 사용하여 using module 형식을 로드합니다. 제네릭에서 상속할 때 사용자 지정 형식이 형식 매개 변수로 사용할 수 있는 해결 방법은 없습니다.

참고 항목