about_Classes_and_DSC

Deskripsi singkat

Menjelaskan bagaimana Anda dapat menggunakan kelas untuk dikembangkan di PowerShell dengan Desired State Configuration (DSC).

Deskripsi panjang

Mulai Windows PowerShell 5.0, bahasa ditambahkan untuk menentukan kelas dan jenis lain yang ditentukan pengguna, dengan menggunakan sintaks formal dan semantik yang mirip dengan bahasa pemrograman berorientasi objek lainnya. Tujuannya adalah untuk memungkinkan pengembang dan profesional TI merangkul PowerShell untuk berbagai kasus penggunaan yang lebih luas, menyederhanakan pengembangan artefak PowerShell seperti sumber daya DSC, dan mempercepat cakupan permukaan manajemen.

Skenario yang didukung

Skenario berikut didukung:

  • Tentukan sumber daya DSC dan jenis terkaitnya dengan menggunakan bahasa PowerShell.
  • Tentukan jenis kustom di PowerShell dengan menggunakan konstruksi pemrograman berorientasi objek yang sudah dikenal, seperti kelas, properti, metode, dan pewarisan.
  • Jenis debug dengan menggunakan bahasa PowerShell.
  • Hasilkan dan tangani pengecualian dengan menggunakan mekanisme formal, dan pada tingkat yang tepat.

Menentukan sumber daya DSC dengan kelas

Selain perubahan sintaksis, perbedaan utama antara sumber daya DSC yang ditentukan kelas dan penyedia sumber daya DSC cmdlet adalah item berikut:

  • File Management Object Format (MOF) tidak diperlukan.
  • Subfolder DSCResource di folder modul tidak diperlukan.
  • File modul PowerShell dapat berisi beberapa kelas sumber daya DSC.

Membuat penyedia sumber daya DSC yang ditentukan kelas

Contoh berikut adalah penyedia sumber daya DSC yang ditentukan kelas yang disimpan sebagai modul, MyDSCResource.psm1. Anda harus selalu menyertakan properti kunci di penyedia sumber daya DSC yang ditentukan kelas.

enum Ensure
{
    Absent
    Present
}

<#
    This resource manages the file in a specific path.
    [DscResource()] indicates the class is a DSC resource
#>

[DscResource()]
class FileResource
{
    <#
        This property is the fully qualified path to the file that is
        expected to be present or absent.

        The [DscProperty(Key)] attribute indicates the property is a
        key and its value uniquely identifies a resource instance.
        Defining this attribute also means the property is required
        and DSC will ensure a value is set before calling the resource.

        A DSC resource must define at least one key property.
    #>
    [DscProperty(Key)]
    [string]$Path

    <#
        This property indicates if the settings should be present or absent
        on the system. For present, the resource ensures the file pointed
        to by $Path exists. For absent, it ensures the file point to by
        $Path does not exist.

        The [DscProperty(Mandatory)] attribute indicates the property is
        required and DSC will guarantee it is set.

        If Mandatory is not specified or if it is defined as
        Mandatory=$false, the value is not guaranteed to be set when DSC
        calls the resource.  This is appropriate for optional properties.
    #>
    [DscProperty(Mandatory)]
    [Ensure] $Ensure

    <#
        This property defines the fully qualified path to a file that will
        be placed on the system if $Ensure = Present and $Path does not
        exist.

        NOTE: This property is required because [DscProperty(Mandatory)] is
        set.
    #>
    [DscProperty(Mandatory)]
    [string] $SourcePath

    <#
        This property reports the file's create timestamp.

        [DscProperty(NotConfigurable)] attribute indicates the property is
        not configurable in DSC configuration.  Properties marked this way
        are populated by the Get() method to report additional details
        about the resource when it is present.

    #>
    [DscProperty(NotConfigurable)]
    [Nullable[datetime]] $CreationTime

    <#
        This method is equivalent of the Set-TargetResource script function.
        It sets the resource to the desired state.
    #>
    [void] Set()
    {
        $fileExists = $this.TestFilePath($this.Path)
        if($this.ensure -eq [Ensure]::Present)
        {
            if(-not $fileExists)
            {
                $this.CopyFile()
            }
        }
        else
        {
            if($fileExists)
            {
                Write-Verbose -Message "Deleting the file $($this.Path)"
                Remove-Item -LiteralPath $this.Path -Force
            }
        }
    }

    <#

        This method is equivalent of the Test-TargetResource script
        function. It should return True or False, showing whether the
        resource is in a desired state.
    #>
    [bool] Test()
    {
        $present = $this.TestFilePath($this.Path)

        if($this.Ensure -eq [Ensure]::Present)
        {
            return $present
        }
        else
{
            return -not $present
        }
    }

    <#
        This method is equivalent of the Get-TargetResource script function.
        The implementation should use the keys to find appropriate
        resources. This method returns an instance of this class with the
        updated key properties.
    #>
    [FileResource] Get()
    {
        $present = $this.TestFilePath($this.Path)

        if ($present)
        {
            $file = Get-ChildItem -LiteralPath $this.Path
            $this.CreationTime = $file.CreationTime
            $this.Ensure = [Ensure]::Present
        }
        else
        {
            $this.CreationTime = $null
            $this.Ensure = [Ensure]::Absent
        }
        return $this
    }

    <#
        Helper method to check if the file exists and it is correct file
    #>
    [bool] TestFilePath([string] $location)
    {
        $present = $true

        $item = Get-ChildItem -LiteralPath $location -ea Ignore
        if ($null -eq $item)
        {
            $present = $false
        }
        elseif( $item.PSProvider.Name -ne "FileSystem")
        {
            throw "Path $($location) is not a file path."
        }
        elseif($item.PSIsContainer)
        {
            throw "Path $($location) is a directory path."
        }
        return $present
    }

    <#
        Helper method to copy file from source to path
    #>
    [void] CopyFile()
    {
        if(-not $this.TestFilePath($this.SourcePath))
        {
            throw "SourcePath $($this.SourcePath) is not found."
        }

        [System.IO.FileInfo]
        $destFileInfo = new-object System.IO.FileInfo($this.Path)

        if (-not $destFileInfo.Directory.Exists)
        {
            $FullName = $destFileInfo.Directory.FullName
            $Message = "Creating directory $FullName"

            Write-Verbose -Message $Message

            #use CreateDirectory instead of New-Item to avoid code
            # to handle the non-terminating error
            [System.IO.Directory]::CreateDirectory($FullName)
        }

        if(Test-Path -LiteralPath $this.Path -PathType Container)
        {
            throw "Path $($this.Path) is a directory path"
        }

        Write-Verbose -Message "Copying $this.SourcePath to $this.Path"

        #DSC engine catches and reports any error that occurs
        Copy-Item -Path $this.SourcePath -Destination $this.Path -Force
    }
}

Membuat manifes modul

Setelah membuat penyedia sumber daya DSC yang ditentukan kelas, dan menyimpannya sebagai modul, buat manifes modul untuk modul. Untuk membuat sumber daya berbasis kelas tersedia untuk mesin DSC, Anda harus menyertakan DscResourcesToExport pernyataan dalam file manifes yang menginstruksikan modul untuk mengekspor sumber daya. Dalam contoh ini, manifes modul berikut disimpan sebagai MyDscResource.psd1.

@{

# Script module or binary module file associated with this manifest.
RootModule = 'MyDscResource.psm1'

DscResourcesToExport = 'FileResource'

# Version number of this module.
ModuleVersion = '1.0'

# ID used to uniquely identify this module
GUID = '81624038-5e71-40f8-8905-b1a87afe22d7'

# Author of this module
Author = 'Microsoft Corporation'

# Company or vendor of this module
CompanyName = 'Microsoft Corporation'

# Copyright statement for this module
Copyright = '(c) 2014 Microsoft. All rights reserved.'

# Description of the functionality provided by this module
# Description = ''

# Minimum version of the PowerShell engine required by this module
PowerShellVersion = '5.0'

# Name of the PowerShell host required by this module
# PowerShellHostName = ''

}

Menyebarkan penyedia sumber daya DSC

Sebarkan penyedia sumber daya DSC baru dengan membuat folder MyDscResource di $pshome\Modules atau $env:SystemDrive\ProgramFiles\WindowsPowerShell\Modules.

Anda tidak perlu membuat subfolder DSCResource. Salin file manifes modul dan modul (MyDscResource.psm1 dan MyDscResource.psd1) ke folder MyDscResource.

Dari titik ini, Anda membuat dan menjalankan skrip konfigurasi seperti yang Anda lakukan dengan sumber daya DSC apa pun.

Buat skrip konfigurasi DSC

Setelah menyimpan file kelas dan manifes dalam struktur folder seperti yang dijelaskan sebelumnya, Anda dapat membuat konfigurasi yang menggunakan sumber daya baru. Konfigurasi berikut mereferensikan modul MyDSCResource. Simpan konfigurasi sebagai skrip, MyResource.ps1.

Untuk informasi tentang cara menjalankan konfigurasi DSC, lihat Gambaran Umum Konfigurasi Status yang Diinginkan Windows PowerShell.

Sebelum Anda menjalankan konfigurasi, buat C:\test.txt. Konfigurasi memeriksa apakah file ada di c:\test\test.txt. Jika file tidak ada, konfigurasi menyalin file dari C:\test.txt.

Configuration Test
{
    Import-DSCResource -ModuleName MyDscResource
    FileResource file
    {
        Path = "C:\test\test.txt"
        SourcePath = "C:\test.txt"
        Ensure = "Present"
    }
}
Test
Start-DscConfiguration -Wait -Force Test

Jalankan skrip ini seperti yang Anda lakukan pada skrip konfigurasi DSC apa pun. Untuk memulai konfigurasi, di konsol PowerShell yang ditingkatkan, jalankan hal berikut:

PS C:\test> .\MyResource.ps1

Pewarisan di kelas PowerShell

Mendeklarasikan kelas dasar untuk kelas PowerShell

Anda dapat mendeklarasikan kelas PowerShell sebagai jenis dasar untuk kelas PowerShell lain, seperti yang ditunjukkan dalam contoh berikut, di mana buah adalah jenis dasar untuk apel.

class fruit
{
    [int]sold() {return 100500}
}

class apple : fruit {}
    [apple]::new().sold() # return 100500

Mendeklarasikan antarmuka yang diimplementasikan untuk kelas PowerShell

Anda dapat mendeklarasikan antarmuka yang diimplementasikan setelah jenis dasar, atau segera setelah titik dua (:) jika tidak ada jenis dasar yang ditentukan. Pisahkan semua nama jenis dengan menggunakan koma. Ini mirip dengan sintaks C#.

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

class MyComparableTest : test, system.IComparable
{
    [int] CompareTo([object] $obj)
    {
        return 0;
    }
}

Konstruktor kelas dasar panggilan

Untuk memanggil konstruktor kelas dasar dari subkelas, tambahkan base kata kunci, seperti yang ditunjukkan dalam contoh berikut:

class A {
    [int]$a
    A([int]$a)
    {
        $this.a = $a
    }
}

class B : A
{
    B() : base(103) {}
}

    [B]::new().a # return 103

Jika kelas dasar memiliki konstruktor default (tanpa parameter), Anda dapat menghilangkan panggilan konstruktor eksplisit, seperti yang ditunjukkan.

class C : B
{
    C([int]$c) {}
}

Memanggil metode kelas dasar

Anda dapat mengambil alih metode yang ada dalam subkelas. Untuk melakukan penimpaan, nyatakan metode dengan menggunakan nama dan tanda tangan yang sama.

class baseClass
{
    [int]days() {return 100500}
}
class childClass1 : baseClass
{
    [int]days () {return 200600}
}

    [childClass1]::new().days() # return 200600

Untuk memanggil metode kelas dasar dari implementasi yang ditimpa, transmisikan ke kelas ([baseclass]$this) dasar saat pemanggilan.

class childClass2 : baseClass
{
    [int]days()
    {
        return 3 * ([baseClass]$this).days()
    }
}

    [childClass2]::new().days() # return 301500

Semua metode PowerShell bersifat virtual. Anda dapat menyembunyikan metode .NET non-virtual dalam subkelas dengan menggunakan sintaks yang sama seperti yang Anda lakukan untuk penimpaan: menyatakan metode dengan nama dan tanda tangan yang sama.

class MyIntList : system.collections.generic.list[int]
{
    # Add is final in system.collections.generic.list
    [void] Add([int]$arg)
    {
        ([system.collections.generic.list[int]]$this).Add($arg * 2)
    }
}

$list = [MyIntList]::new()
$list.Add(100)
$list[0] # return 200

Batasan saat ini dengan pewarisan kelas

Batasan dengan pewarisan kelas adalah bahwa tidak ada sintaks untuk mendeklarasikan antarmuka di PowerShell.

Menentukan jenis kustom di PowerShell

Windows PowerShell 5.0 memperkenalkan beberapa elemen bahasa.

Kata kunci kelas

Mendefinisikan kelas baru. Kata class kunci adalah jenis .NET Framework yang benar. Anggota kelas bersifat publik.

class MyClass
{
}

Kata kunci dan enumerasi Enum

Dukungan untuk enum kata kunci ditambahkan dan merupakan perubahan yang melanggar. Pemisah enum saat ini adalah baris baru. Solusi sementara bagi mereka yang sudah menggunakan enum adalah menyisipkan ampersand (&) sebelum kata. Batasan saat ini: Anda tidak dapat menentukan enumerator dalam hal itu sendiri, tetapi Anda dapat menginisialisasi enum dalam hal yang lain enum, seperti yang ditunjukkan dalam contoh berikut:

Tipe dasar saat ini tidak dapat ditentukan. Jenis dasar selalu [int].

enum Color2
{
    Yellow = [Color]::Blue
}

Nilai enumerator harus berupa konstanta waktu penguraian. Nilai enumerator tidak dapat diatur ke hasil perintah yang dipanggil.

enum MyEnum
{
    Enum1
    Enum2
    Enum3 = 42
    Enum4 = [int]::MaxValue
}

Enum mendukung operasi aritmatika, seperti yang ditunjukkan dalam contoh berikut:

enum SomeEnum { Max = 42 }
enum OtherEnum { Max = [SomeEnum]::Max + 1 }

Kata kunci tersembunyi

Kata hidden kunci, yang diperkenalkan di Windows PowerShell 5.0, menyembunyikan anggota kelas dari hasil defaultGet-Member. Tentukan properti tersembunyi seperti yang diperlihatkan dalam baris berikut:

hidden [type] $classmember = <value>

Anggota tersembunyi tidak ditampilkan dengan menggunakan penyelesaian tab atau IntelliSense, kecuali penyelesaian terjadi di kelas yang menentukan anggota tersembunyi.

Atribut baru, System.Management.Automation.HiddenAttribute, ditambahkan, sehingga kode C# dapat memiliki semantik yang sama dalam PowerShell.

Untuk informasi selengkapnya, lihat [about_Hidden[(/powershell/module/microsoft.powershell.core/about/about_hidden).

Import-DscResource

Import-DscResource sekarang menjadi kata kunci dinamis yang sebenarnya. PowerShell mengurai modul akar modul yang ditentukan, mencari kelas yang berisi atribut DscResource.

Properti

Bidang baru, ImplementingAssembly, ditambahkan ke ModuleInfo. Jika skrip mendefinisikan kelas, atau rakitan yang dimuat untuk modul ImplementingAssembly biner diatur ke rakitan dinamis yang dibuat untuk modul skrip. Ini tidak diatur ketika ModuleType = Manifest.

Refleksi pada ImplementingAssembly bidang menemukan sumber daya dalam modul. Ini berarti Anda dapat menemukan sumber daya yang ditulis dalam PowerShell atau bahasa terkelola lainnya.

Bidang dengan penginisialisasi.

[int] $i = 5

Statis didukung dan berfungsi seperti atribut, mirip dengan batasan jenis, sehingga dapat ditentukan dalam urutan apa pun.

static [int] $count = 0

Jenis bersifat opsional.

$s = "hello"

Semua anggota bersifat publik. Properti memerlukan garis baru atau titik koma. Jika tidak ada jenis objek yang ditentukan, jenis properti adalah Objek.

Konstruktor dan instansiasi

Kelas PowerShell dapat memiliki konstruktor yang memiliki nama yang sama dengan kelasnya. Konstruktor dapat kelebihan beban. Konstruktor statis didukung. Properti dengan ekspresi inisialisasi diinisialisasi sebelum menjalankan kode apa pun dalam konstruktor. Properti statis diinisialisasi sebelum isi konstruktor statis, dan properti instans diinisialisasi sebelum isi konstruktor non-statis. Saat ini, tidak ada sintaks untuk memanggil konstruktor dari konstruktor lain seperti sintaks C#: ": this()"). Solusinya adalah menentukan metode Init umum.

Berikut ini adalah cara membuat instans kelas:

  • Membuat instans dengan menggunakan konstruktor default. Perhatikan bahwa New-Object tidak didukung dalam rilis ini.

    $a = [MyClass]::new()

  • Memanggil konstruktor dengan parameter .

    $b = [MyClass]::new(42)

  • Meneruskan array ke konstruktor dengan beberapa parameter

    $c = [MyClass]::new(@(42,43,44), "Hello")

Untuk rilis ini, nama jenis hanya terlihat secara leksikal, yang berarti tidak terlihat di luar modul atau skrip yang menentukan kelas . Fungsi dapat mengembalikan instans kelas yang ditentukan dalam PowerShell, dan instans berfungsi dengan baik di luar modul atau skrip.

Parameter Get-MemberStatis mencantumkan konstruktor, sehingga Anda dapat melihat kelebihan beban seperti metode lainnya. Performa sintaks ini juga jauh lebih cepat daripada New-Object.

Metode pseudo-static bernama baru berfungsi dengan jenis .NET, seperti yang ditunjukkan dalam contoh berikut. [hashtable]::new()

Sekarang Anda dapat melihat kelebihan beban konstruktor dengan Get-Member, atau seperti yang ditunjukkan dalam contoh ini:

[hashtable]::new
OverloadDefinitions
-------------------
hashtable new()
hashtable new(int capacity)
hashtable new(int capacity, float loadFactor)

Metode

Metode kelas PowerShell diimplementasikan sebagai ScriptBlock yang hanya memiliki blok akhir. Semua metode bersifat publik. Berikut ini menunjukkan contoh mendefinisikan metode bernama DoSomething.

class MyClass
{
    DoSomething($x)
    {
        $this._doSomething($x)       # method syntax
    }
    private _doSomething($a) {}
}

Pemanggilan metode

Metode yang kelebihan beban didukung. Metode yang kelebihan beban dinamai sama dengan metode yang ada tetapi dibedakan oleh nilai yang ditentukan.

$b = [MyClass]::new()
$b.DoSomething(42)

Invokasi

Lihat Pemanggilan metode.

Atribut

Tiga atribut baru ditambahkan: DscResource, , DscResourceKeydan DscResourceMandatory.

Jenis yang ditampilkan

Jenis pengembalian adalah kontrak. Nilai yang dikembalikan dikonversi ke jenis yang diharapkan. Jika tidak ada jenis pengembalian yang ditentukan, jenis pengembalian akan batal. Tidak ada streaming objek dan objek yang tidak dapat ditulis ke alur baik secara sengaja atau tidak sengaja.

Pencakupan leksikal variabel

Berikut ini menunjukkan contoh cara kerja pencakupan leksikal dalam rilis ini.

$d = 42  # Script scope

function bar
{
    $d = 0  # Function scope
    [MyClass]::DoSomething()
}

class MyClass
{
    static [object] DoSomething()
    {
        return $d  # error, not found dynamically
        return $script:d # no error

        $d = $script:d
        return $d # no error, found lexically
    }
}

$v = bar
$v -eq $d # true

Contoh: Membuat kelas kustom

Contoh berikut membuat beberapa kelas kustom baru untuk mengimplementasikan Html Dynamic Stylesheet Language (DSL). Contoh menambahkan fungsi pembantu untuk membuat jenis elemen tertentu sebagai bagian dari kelas elemen, seperti gaya judul dan tabel, karena jenis tidak dapat digunakan di luar lingkup modul.

# Classes that define the structure of the document
#
class Html
{
    [string] $docType
    [HtmlHead] $Head
    [Element[]] $Body

    [string] Render()
    {
        $text = "<html>`n<head>`n"
        $text += $Head
        $text += "`n</head>`n<body>`n"
        $text += $Body -join "`n" # Render all of the body elements
        $text += "</body>`n</html>"
        return $text
    }
    [string] ToString() { return $this.Render() }
}

class HtmlHead
{
    $Title
    $Base
    $Link
    $Style
    $Meta
    $Script
    [string] Render() { return "<title>$Title</title>" }
    [string] ToString() { return $this.Render() }
}

class Element
{
    [string] $Tag
    [string] $Text
    [hashtable] $Attributes
    [string] Render() {
        $attributesText= ""
        if ($Attributes)
        {
            foreach ($attr in $Attributes.Keys)
            {
                $attributesText = " $attr=`"$($Attributes[$attr])`""
            }
        }

        return "<${tag}${attributesText}>$text</$tag>`n"
    }
    [string] ToString() { return $this.Render() }
}

#
# Helper functions for creating specific element types on top of the classes.
# These are required because types aren't visible outside of the module.
#
function H1 {[Element] @{Tag = "H1"; Text = $args.foreach{$_} -join " "}}
function H2 {[Element] @{Tag = "H2"; Text = $args.foreach{$_} -join " "}}
function H3 {[Element] @{Tag = "H3"; Text = $args.foreach{$_} -join " "}}
function P  {[Element] @{Tag = "P" ; Text = $args.foreach{$_} -join " "}}
function B  {[Element] @{Tag = "B" ; Text = $args.foreach{$_} -join " "}}
function I  {[Element] @{Tag = "I" ; Text = $args.foreach{$_} -join " "}}
function HREF
{
    param (
        $Name,
        $Link
    )

    return [Element] @{
        Tag = "A"
        Attributes = @{ HREF = $link }
        Text = $name
    }
}
function Table
{
    param (
        [Parameter(Mandatory)]
        [object[]]
            $Data,
        [Parameter()]
        [string[]]
            $Properties = "*",
        [Parameter()]
        [hashtable]
            $Attributes = @{ border=2; cellpadding=2; cellspacing=2 }
    )

    $bodyText = ""
    # Add the header tags
    $bodyText +=  $Properties.foreach{TH $_}
    # Add the rows
    $bodyText += foreach ($row in $Data)
                {
                            TR (-join $Properties.Foreach{ TD ($row.$_) } )
                }

    $table = [Element] @{
                Tag = "Table"
                Attributes = $Attributes
                Text = $bodyText
            }
    $table
}
function TH  {([Element] @{Tag="TH"; Text=$args.foreach{$_} -join " "})}
function TR  {([Element] @{Tag="TR"; Text=$args.foreach{$_} -join " "})}
function TD  {([Element] @{Tag="TD"; Text=$args.foreach{$_} -join " "})}

function Style
{
    return  [Element]  @{
        Tag = "style"
        Text = "$args"
    }
}

# Takes a hash table, casts it to and HTML document
# and then returns the resulting type.
#
function Html ([HTML] $doc) { return $doc }

Lihat juga

about_Enum

about_Hidden

about_Language_Keywords

about_Methods

Membangun Sumber Daya Konfigurasi Status yang Diinginkan PowerShell Kustom