about_Classes_and_DSC
Krótki opis
Opisuje sposób tworzenia klas w programie PowerShell przy użyciu Desired State Configuration (DSC).
Długi opis
Począwszy od Windows PowerShell 5.0, język został dodany do definiowania klas i innych typów zdefiniowanych przez użytkownika przy użyciu składni formalnej i semantyki, które są podobne do innych języków programowania obiektowego. Celem jest umożliwienie deweloperom i specjalistom IT wdrożenia programu PowerShell w szerszym zakresie przypadków użycia, uproszczenie opracowywania artefaktów programu PowerShell, takich jak zasoby DSC i przyspieszenie pokrycia powierzchni zarządzania.
Obsługiwane scenariusze
Obsługiwane są następujące scenariusze:
- Zdefiniuj zasoby DSC i skojarzone z nimi typy przy użyciu języka programu PowerShell.
- Zdefiniuj typy niestandardowe w programie PowerShell przy użyciu znanych konstrukcji programowania zorientowanych na obiekty, takich jak klasy, właściwości, metody i dziedziczenie.
- Debugowanie typów przy użyciu języka programu PowerShell.
- Generowanie i obsługa wyjątków przy użyciu mechanizmów formalnych i na odpowiednim poziomie.
Definiowanie zasobów DSC przy użyciu klas
Oprócz zmian składni główne różnice między zasobem DSC zdefiniowanym przez klasę a dostawcą zasobów DSC poleceń cmdlet to następujące elementy:
- Plik MOF (Management Object Format) nie jest wymagany.
- Podfolder DSCResource w folderze modułu nie jest wymagany.
- Plik modułu programu PowerShell może zawierać wiele klas zasobów DSC.
Tworzenie dostawcy zasobów DSC zdefiniowanego przez klasę
Poniższy przykład to dostawca zasobów DSC zdefiniowany przez klasę, który jest zapisywany jako moduł MyDSCResource.psm1. Zawsze należy uwzględnić właściwość klucza u dostawcy zasobów DSC zdefiniowanego przez klasę.
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
}
}
Tworzenie manifestu modułu
Po utworzeniu dostawcy zasobów DSC zdefiniowanego przez klasę i zapisaniu go jako modułu utwórz manifest modułu dla modułu. Aby udostępnić zasób oparty na klasie aparatowi DSC, należy dołączyć instrukcję DscResourcesToExport
w pliku manifestu, który nakazuje modułowi wyeksportowanie zasobu. W tym przykładzie następujący manifest modułu jest zapisywany 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 = ''
}
Wdrażanie dostawcy zasobów DSC
Wdróż nowego dostawcę zasobów DSC, tworząc folder MyDscResource w systemie $pshome\Modules
lub $env:SystemDrive\ProgramFiles\WindowsPowerShell\Modules
.
Nie trzeba tworzyć podfolderu DSCResource. Skopiuj pliki manifestu modułu i modułu (MyDscResource.psm1 i MyDscResource.psd1) do folderu MyDscResource.
W tym momencie utworzysz i uruchomisz skrypt konfiguracji, tak jak w przypadku dowolnego zasobu DSC.
Tworzenie skryptu konfiguracji DSC
Po zapisaniu plików klasy i manifestu w strukturze folderów zgodnie z wcześniejszym opisem można utworzyć konfigurację, która używa nowego zasobu. Poniższa konfiguracja odwołuje się do modułu MyDSCResource. Zapisz konfigurację jako skrypt, MyResource.ps1.
Aby uzyskać informacje o sposobie uruchamiania konfiguracji DSC, zobacz Windows PowerShell Desired State Configuration Omówienie.
Przed uruchomieniem konfiguracji utwórz C:\test.txt
plik . Konfiguracja sprawdza, czy plik istnieje w lokalizacji c:\test\test.txt
. Jeśli plik nie istnieje, konfiguracja kopiuje plik z C:\test.txt
pliku .
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
Uruchom ten skrypt, tak jak każdy skrypt konfiguracji DSC. Aby uruchomić konfigurację, w konsoli programu PowerShell z podwyższonym poziomem uprawnień uruchom następujące polecenie:
PS C:\test> .\MyResource.ps1
Dziedziczenie w klasach programu PowerShell
Deklarowanie klas bazowych dla klas programu PowerShell
Klasę programu PowerShell można zadeklarować jako typ podstawowy dla innej klasy programu PowerShell, jak pokazano w poniższym przykładzie, w którym owoce są typem podstawowym dla jabłek.
class fruit
{
[int]sold() {return 100500}
}
class apple : fruit {}
[apple]::new().sold() # return 100500
Deklarowanie wdrożonych interfejsów dla klas programu PowerShell
Zaimplementowane interfejsy można zadeklarować po typach podstawowych lub bezpośrednio po dwukropku (:
), jeśli nie określono typu podstawowego. Rozdziel wszystkie nazwy typów przy użyciu przecinków. Jest to podobne do składni języka C#.
class MyComparable : system.IComparable
{
[int] CompareTo([object] $obj)
{
return 0;
}
}
class MyComparableTest : test, system.IComparable
{
[int] CompareTo([object] $obj)
{
return 0;
}
}
Wywoływanie konstruktorów klasy bazowej
Aby wywołać konstruktor klasy bazowej z podklasy, dodaj base
słowo kluczowe, jak pokazano w poniższym przykładzie:
class A {
[int]$a
A([int]$a)
{
$this.a = $a
}
}
class B : A
{
B() : base(103) {}
}
[B]::new().a # return 103
Jeśli klasa bazowa ma konstruktor domyślny (bez parametrów), można pominąć jawne wywołanie konstruktora, jak pokazano.
class C : B
{
C([int]$c) {}
}
Wywoływanie metod klasy bazowej
Istniejące metody można zastąpić w podklasach. Aby przesłonić, zadeklaruj metody przy użyciu tej samej nazwy i podpisu.
class baseClass
{
[int]days() {return 100500}
}
class childClass1 : baseClass
{
[int]days () {return 200600}
}
[childClass1]::new().days() # return 200600
Aby wywołać metody klasy bazowej z przesłonięć implementacje, rzutuj do klasy ([baseclass]$this)
bazowej przy wywołaniu.
class childClass2 : baseClass
{
[int]days()
{
return 3 * ([baseClass]$this).days()
}
}
[childClass2]::new().days() # return 301500
Wszystkie metody programu PowerShell są wirtualne. Metody platformy .NET niewirtualnej można ukryć w podklasie przy użyciu tej samej składni, co w przypadku przesłonięcia: deklaruj metody o tej samej nazwie i podpisie.
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
Bieżące ograniczenia dotyczące dziedziczenia klas
Ograniczenie dziedziczenia klasy polega na tym, że nie ma składni do deklarowania interfejsów w programie PowerShell.
Definiowanie typów niestandardowych w programie PowerShell
Windows PowerShell 5.0 wprowadzono kilka elementów języka.
Słowo kluczowe klasy
Definiuje nową klasę.
Słowo class
kluczowe jest prawdziwym typem .NET Framework.
Elementy członkowskie klasy są publiczne.
class MyClass
{
}
Wyliczenia i słowa kluczowe wyliczenia
Dodano obsługę słowa kluczowego enum
i jest to zmiana powodująca niezgodność. Ogranicznik enum
jest obecnie nowym wierszem. Obejściem dla tych, którzy już używają enum
, jest wstawienie znaku ampersand (&
) przed wyrazem. Bieżące ograniczenia: nie można zdefiniować modułu wyliczającego pod względem siebie, ale można zainicjować enum
pod względem innego enum
elementu , jak pokazano w poniższym przykładzie:
Nie można obecnie określić typu podstawowego. Typ podstawowy to zawsze [int].
enum Color2
{
Yellow = [Color]::Blue
}
Wartość modułu wyliczającego musi być stałą czasu analizy. Nie można ustawić wartości modułu wyliczającego na wynik wywołanego polecenia.
enum MyEnum
{
Enum1
Enum2
Enum3 = 42
Enum4 = [int]::MaxValue
}
Enum
obsługuje operacje arytmetyczne, jak pokazano w poniższym przykładzie:
enum SomeEnum { Max = 42 }
enum OtherEnum { Max = [SomeEnum]::Max + 1 }
Ukryte słowo kluczowe
Słowo hidden
kluczowe wprowadzone w Windows PowerShell 5.0 ukrywa elementy członkowskie klasy przed domyślnymi Get-Member
wynikami. Określ właściwość ukrytą, jak pokazano w następującym wierszu:
hidden [type] $classmember = <value>
Ukryte elementy członkowskie nie są wyświetlane przy użyciu uzupełniania karty lub funkcji IntelliSense, chyba że ukończenie występuje w klasie definiującej ukryty element członkowski.
Dodano nowy atrybut System.Management.Automation.HiddenAttribute, aby kod języka C# mógł mieć te same semantyki w programie PowerShell.
Aby uzyskać więcej informacji, zobacz [about_Hidden[(/powershell/module/microsoft.powershell.core/about/about_hidden).
Import-DscResource
Import-DscResource
jest teraz prawdziwym dynamicznym słowem kluczowym. Program PowerShell analizuje moduł główny określonego modułu, wyszukując klasy zawierające atrybut DscResource.
Właściwości
Nowe pole, ImplementingAssembly
, zostało dodane do ModuleInfo
. Jeśli skrypt definiuje klasy lub załadowany zestaw dla modułów ImplementingAssembly
binarnych jest ustawiony na zestaw dynamiczny utworzony dla modułu skryptu. Nie jest ustawiana, gdy moduleType = Manifest.
Odbicie w ImplementingAssembly
polu odnajduje zasoby w module. Oznacza to, że można odnajdywać zasoby napisane w programie PowerShell lub w innych językach zarządzanych.
Pola z inicjatorami.
[int] $i = 5
Statyczny jest obsługiwany i działa jak atrybut, podobny do ograniczeń typu, dzięki czemu można go określić w dowolnej kolejności.
static [int] $count = 0
Typ jest opcjonalny.
$s = "hello"
Wszyscy członkowie są publiczni. Właściwości wymagają nowego wiersza lub średnika. Jeśli nie określono typu obiektu, typ właściwości to Obiekt.
Konstruktory i wystąpienia
Klasy programu PowerShell mogą mieć konstruktory, które mają taką samą nazwę jak ich klasa. Konstruktory mogą być przeciążone. Konstruktory statyczne są obsługiwane.
Właściwości z wyrażeniami inicjowania są inicjowane przed uruchomieniem dowolnego kodu w konstruktorze. Właściwości statyczne są inicjowane przed treścią konstruktora statycznego, a właściwości wystąpienia są inicjowane przed treścią konstruktora niestatycznego. Obecnie nie ma składni wywoływania konstruktora z innego konstruktora, takiego jak składnia języka C#: ": this()")
. Obejście polega na zdefiniowaniu typowej metody Init.
Poniżej przedstawiono sposoby tworzenia wystąpień klas:
Utworzenie wystąpienia przy użyciu konstruktora domyślnego. Należy pamiętać, że
New-Object
ta wersja nie jest obsługiwana.$a = [MyClass]::new()
Wywoływanie konstruktora za pomocą parametru.
$b = [MyClass]::new(42)
Przekazywanie tablicy do konstruktora z wieloma parametrami
$c = [MyClass]::new(@(42,43,44), "Hello")
W tej wersji nazwa typu jest widoczna tylko leksykalnie, co oznacza, że nie jest widoczna poza modułem ani skryptem definiującym klasę. Funkcje mogą zwracać wystąpienia klasy zdefiniowanej w programie PowerShell, a wystąpienia działają dobrze poza modułem lub skryptem.
Parametr Get-Member
statyczny zawiera listę konstruktorów, dzięki czemu można wyświetlać przeciążenia, takie jak każda inna metoda. Wydajność tej składni jest również znacznie szybsza niż New-Object
.
Metoda pseudostatyczna o nazwie new działa z typami platformy .NET, jak pokazano w poniższym przykładzie. [hashtable]::new()
Teraz można zobaczyć przeciążenia konstruktora z wartością Get-Member
lub, jak pokazano w tym przykładzie:
[hashtable]::new
OverloadDefinitions
-------------------
hashtable new()
hashtable new(int capacity)
hashtable new(int capacity, float loadFactor)
Metody
Metoda klasy programu PowerShell jest implementowana jako scriptBlock , która ma tylko blok końcowy. Wszystkie metody są publiczne. Poniżej przedstawiono przykład definiowania metody o nazwie DoSomething.
class MyClass
{
DoSomething($x)
{
$this._doSomething($x) # method syntax
}
private _doSomething($a) {}
}
Wywołanie metody
Metody przeciążone są obsługiwane. Metody przeciążone mają taką samą nazwę jak istniejąca metoda, ale różnią się od ich określonych wartości.
$b = [MyClass]::new()
$b.DoSomething(42)
Invocation
Zobacz Wywołanie metody.
Atrybuty
Dodano trzy nowe atrybuty: DscResource
, DscResourceKey
i DscResourceMandatory
.
Typy zwracane
Zwracany typ to kontrakt. Zwracana wartość jest konwertowana na oczekiwany typ. Jeśli nie określono typu zwracanego, zwracany typ jest void. Nie można zapisywać strumieniowo obiektów i obiektów do potoku celowo lub przypadkowo.
Określanie zakresu zmiennych leksykalnych
Poniżej przedstawiono przykład działania zakresu leksykalnego w tej wersji.
$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
Przykład: Tworzenie klas niestandardowych
Poniższy przykład tworzy kilka nowych, niestandardowych klas w celu zaimplementowania języka HTML Dynamic Stylesheet Language (DSL). W przykładzie dodano funkcje pomocnicze do tworzenia określonych typów elementów w ramach klasy elementów, takich jak style nagłówków i tabele, ponieważ typy nie mogą być używane poza zakresem modułu.
# 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 }
Zobacz też
Tworzenie niestandardowych zasobów programu PowerShell Desired State Configuration
Opinia
https://aka.ms/ContentUserFeedback.
Dostępne już wkrótce: W 2024 r. będziemy stopniowo wycofywać zgłoszenia z serwisu GitHub jako mechanizm przesyłania opinii na temat zawartości i zastępować go nowym systemem opinii. Aby uzyskać więcej informacji, sprawdź:Prześlij i wyświetl opinię dla