Sdílet prostřednictvím


about_Classes_and_DSC

Krátký popis

Popisuje, jak můžete pomocí tříd vyvíjet v PowerShellu pomocí DSC (Desired State Configuration).

Dlouhý popis

Počínaje windows PowerShellem 5.0 byl jazyk přidán k definování tříd a dalších uživatelsky definovaných typů pomocí formální syntaxe a sémantiky, které jsou podobné jiným programovacím jazykům orientovaným na objekty. Cílem je umožnit vývojářům a IT odborníkům využívat PowerShell pro širší škálu případů použití, zjednodušit vývoj artefaktů PowerShellu, jako jsou prostředky DSC, a zrychlit pokrytí povrchů správy.

Podporované scénáře

Podporují se následující scénáře:

  • Definujte prostředky DSC a jejich přidružené typy pomocí jazyka PowerShell.
  • Definujte vlastní typy v PowerShellu pomocí známých konstruktorů objektově orientovaného programování, jako jsou třídy, vlastnosti, metody a dědičnost.
  • Ladicí typy pomocí jazyka PowerShellu
  • Generování a zpracování výjimek pomocí formálních mechanismů a na správné úrovni.

Definování prostředků DSC pomocí tříd

Kromě změn syntaxe jsou hlavní rozdíly mezi prostředkem DSC definovaným třídou a poskytovatelem prostředků DSC rutiny následující položky:

  • Soubor MOF (Management Object Format) není povinný.
  • Podsložka DSCResource ve složce modulu není nutná.
  • Soubor modulu PowerShellu může obsahovat více tříd prostředků DSC.

Vytvoření poskytovatele prostředků DSC definovaného třídou

Následující příklad je zprostředkovatel prostředků DSC definovaný třídou, který je uložen jako modul MyDSCResource.psm1. V poskytovateli prostředků DSC definovaném třídou musíte vždy zahrnout klíčovou vlastnost.

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

Vytvoření manifestu modulu

Po vytvoření poskytovatele prostředků DSC definovaného třídou a jeho uložení jako modulu vytvořte manifest modulu. Chcete-li zpřístupnit prostředek založený na třídě pro modul DSC, musíte do souboru manifestu zahrnout DscResourcesToExport příkaz, který dává modulu pokyn k exportu prostředku. V tomto příkladu se následující manifest modulu uloží jako 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 = ''

}

Nasazení poskytovatele prostředků DSC

Nasaďte nového poskytovatele prostředků DSC vytvořením složky MyDscResource v $pshome\Modules nebo $env:SystemDrive\ProgramFiles\WindowsPowerShell\Modules.

Není nutné vytvořit podsložku DSCResource. Zkopírujte soubory manifestu modulu a modulu (MyDscResource.psm1 a MyDscResource.psd1) do složky MyDscResource.

Od tohoto okamžiku vytvoříte a spustíte konfigurační skript stejně jako u jakéhokoli prostředku DSC.

Vytvoření konfiguračního skriptu DSC

Po uložení souborů třídy a manifestu ve struktuře složek, jak je popsáno výše, můžete vytvořit konfiguraci, která používá nový prostředek. Následující konfigurace odkazuje na modul MyDSCResource. Uložte konfiguraci jako skript, MyResource.ps1.

Informace o tom, jak spustit konfiguraci DSC, najdete v tématu Přehled konfigurace požadovaného stavu Windows PowerShell.

Před spuštěním konfigurace vytvořte C:\test.txtsoubor . Konfigurace zkontroluje, zda soubor existuje v .c:\test\test.txt Pokud soubor neexistuje, konfigurace zkopíruje soubor z 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

Spusťte tento skript stejně jako jakýkoli konfigurační skript DSC. Konfiguraci spustíte tak, že v konzole PowerShellu se zvýšenými oprávněními spustíte následující příkaz:

PS C:\test> .\MyResource.ps1

Dědičnost ve třídách PowerShell

Deklarace základních tříd pro třídy PowerShellu

Třídu PowerShell můžete deklarovat jako základní typ pro jinou třídu PowerShellu, jak je znázorněno v následujícím příkladu, ve kterém je fruit základním typem pro apple.

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

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

Deklarace implementovaných rozhraní pro třídy PowerShellu

Implementovaná rozhraní můžete deklarovat za základními typy nebo bezprostředně za dvojtečkou (:), pokud není zadán žádný základní typ. Všechny názvy typů oddělte čárkami. Podobá se syntaxi jazyka C#.

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

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

Volání konstruktorů základní třídy

Chcete-li volat konstruktor základní třídy z podtřídy, přidejte base klíčové slovo, jak je znázorněno v následujícím příkladu:

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

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

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

Pokud má základní třída výchozí konstruktor (bez parametrů), můžete vynechat explicitní volání konstruktoru, jak je znázorněno.

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

Volání metod základní třídy

Existující metody můžete přepsat v podtřídách. Chcete-li provést přepsání, deklarujte metody pomocí stejného názvu a podpisu.

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

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

Chcete-li volat metody základní třídy z přepsaných implementací, přetypujte na základní třídu ([baseclass]$this) při vyvolání.

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

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

Všechny metody PowerShellu jsou virtuální. V podtřídě můžete skrýt jiné než virtuální metody .NET pomocí stejné syntaxe jako u přepsání: deklarujte metody se stejným názvem a podpisem.

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

Aktuální omezení dědičnosti tříd

Omezení dědičnosti tříd spočívá v tom, že v PowerShellu není žádná syntaxe deklarovat rozhraní.

Definování vlastních typů v PowerShellu

Windows PowerShell 5.0 zavedl několik jazykových prvků.

Klíčové slovo třídy

Definuje novou třídu. Klíčové class slovo je skutečný typ rozhraní .NET Framework. Členové třídy jsou veřejné.

class MyClass
{
}

Klíčové slovo výčtu a výčty

Byla přidána podpora pro klíčové enum slovo a jedná se o zásadní změnu. Oddělovač enum je v současné době nový řádek. Řešením pro ty, kteří již používají enum , je vložit před slovo ampersand (&). Současná omezení: nemůžete definovat enumerátor z hlediska sebe sama, ale můžete inicializovat enum z hlediska jiného enum, jak je znázorněno v následujícím příkladu:

Základní typ nelze nyní zadat. Základní typ je vždy [int].

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

Hodnota enumerátoru musí být parsovat časová konstanta. Hodnotu enumerátoru nelze nastavit na výsledek vyvolaného příkazu.

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

Enum Podporuje aritmetické operace, jak je znázorněno v následujícím příkladu:

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

Skryté klíčové slovo

Klíčové hidden slovo, zavedené v Windows PowerShell 5.0, skrývá členy třídy před výchozími Get-Member výsledky. Zadejte skrytou vlastnost, jak je znázorněno na následujícím řádku:

hidden [type] $classmember = <value>

Skryté členy nejsou zobrazeny pomocí dokončování tabulátoru nebo IntelliSense, pokud nedojde k dokončení ve třídě, která definuje skrytý člen.

Byl přidán nový atribut System.Management.Automation.HiddenAttribute, aby kód jazyka C# mohl mít stejnou sémantiku v rámci PowerShellu.

Další informace najdete v tématu [about_Hidden[(/powershell/module/microsoft.powershell.core/about/about_hidden).

Import-DscResource

Import-DscResource je nyní skutečně dynamické klíčové slovo. PowerShell analyzuje kořenový modul zadaného modulu a vyhledá třídy, které obsahují atribut DscResource.

Vlastnosti

Do ImplementingAssemblyModuleInfosouboru . Pokud skript definuje třídy, nebo je načtené sestavení pro binární moduly ImplementingAssembly nastaveno na dynamické sestavení vytvořené pro modul skriptu. Není nastavena, když ModuleType = Manifest.

Reflexe ImplementingAssembly pole objevuje zdroje v modulu. To znamená, že můžete zjistit prostředky napsané v PowerShellu nebo v jiných spravovaných jazycích.

Pole s inicializátory.

[int] $i = 5

Statická je podporovaná a funguje jako atribut, podobně jako omezení typu, takže ho můžete zadat v libovolném pořadí.

static [int] $count = 0

Typ je volitelný.

$s = "hello"

Všichni členové jsou veřejná. Vlastnosti vyžadují buď nový řádek, nebo středník. Pokud není zadán žádný typ objektu, typ vlastnosti je Object.

Konstruktory a vytváření instancí

Třídy PowerShellu můžou mít konstruktory, které mají stejný název jako jejich třída. Konstruktory mohou být přetíženy. Podporují se statické konstruktory. Vlastnosti s inicializačními výrazy se inicializují před spuštěním jakéhokoli kódu v konstruktoru. Statické vlastnosti se inicializují před tělem statického konstruktoru a vlastnosti instance se inicializují před textem nestatického konstruktoru. V současné době neexistuje žádná syntaxe pro volání konstruktoru z jiného konstruktoru, jako je například syntaxe jazyka C#: ": this()"). Alternativním řešením je definovat společnou inicializační metodu.

Následující způsoby vytváření instancí tříd:

  • Vytvoření instance pomocí výchozího konstruktoru Upozorňujeme, že New-Object tato verze není podporována.

    $a = [MyClass]::new()

  • Volání konstruktoru s parametrem

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

  • Předání pole konstruktoru s více parametry

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

V této verzi je název typu viditelný pouze lexiky, což znamená, že není viditelný mimo modul nebo skript, který definuje třídu. Funkce můžou vracet instance třídy definované v PowerShellu a instance fungují dobře mimo modul nebo skript.

Parametr Get-MemberStatic uvádí konstruktory, takže můžete zobrazit přetížení jako jakoukoli jinou metodu. Výkon této syntaxe je také podstatně rychlejší než New-Object.

Pseudostatická metoda s názvem new pracuje s typy .NET, jak je znázorněno v následujícím příkladu. [hashtable]::new()

Nyní můžete vidět přetížení konstruktoru pomocí Get-Member, nebo jak je znázorněno v tomto příkladu:

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

Metody

Metoda třídy PowerShell je implementována jako ScriptBlock , který má pouze koncový blok. Všechny metody jsou veřejné. Následující příklad ukazuje příklad definování metody s názvem DoSomething.

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

Vyvolání metody

Podporované jsou přetížené metody. Přetížené metody jsou pojmenovány stejně jako existující metoda, ale rozlišují se podle jejich zadaných hodnot.

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

Vyvolání

Viz Vyvolání metody.

Atributy

Byly přidány tři nové atributy: DscResource, DscResourceKey, a DscResourceMandatory.

Návratové typy

Návratový typ je kontrakt. Vrácená hodnota se převede na očekávaný typ. Pokud není zadán žádný návratový typ, návratový typ je neplatný. Do kanálu se nedají zapsat žádné streamování objektů a objekty buď záměrně, nebo omylem.

Lexikální obor proměnných

Následující příklad ukazuje, jak v této verzi funguje lexikální obory.

$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

Příklad: Vytvoření vlastních tříd

Následující příklad vytvoří několik nových vlastních tříd pro implementaci HTML Dynamic Stylesheet Language (DSL). Příklad přidá pomocné funkce pro vytvoření konkrétních typů elementů jako součást třídy elementu, jako jsou styly nadpisů a tabulky, protože typy nelze použít mimo rozsah modulu.

# 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 }

Viz také

about_Enum

about_Hidden

about_Language_Keywords

about_Methods

Sestavení vlastních prostředků konfigurace požadovaného stavu PowerShellu