about_Classes_and_DSC
وصف مختصر
توضح هذه المقالة كيف يمكنك استخدام الفئات للتطوير في PowerShell باستخدام Desired State Configuration (DSC).
وصف طويل
بدءا من Windows PowerShell 5.0، تمت إضافة اللغة لتعريف الفئات والأنواع الأخرى المعرفة من قبل المستخدم، باستخدام بناء الجملة الرسمي ودلالات مشابهة للغات البرمجة الأخرى الموجهة للكائنات. الهدف هو تمكين المطورين ومحترفي تكنولوجيا المعلومات من تبني PowerShell لمجموعة أوسع من حالات الاستخدام، وتبسيط تطوير أدوات PowerShell مثل موارد DSC، وتسريع تغطية أسطح الإدارة.
السيناريوهات المدعومة
يتم دعم السيناريوهات التالية:
- تعريف موارد DSC والأنواع المقترنة بها باستخدام لغة PowerShell.
- حدد الأنواع المخصصة في PowerShell باستخدام بنيات برمجة مألوفة موجهة للكائنات، مثل الفئات والخصائص والأساليب والميراث.
- تتبع أخطاء الأنواع باستخدام لغة PowerShell.
- إنشاء ومعالجة الاستثناءات باستخدام آليات رسمية، وعلى المستوى الصحيح.
تعريف موارد DSC مع الفئات
بصرف النظر عن تغييرات بناء الجملة، فإن الاختلافات الرئيسية بين مورد DSC المحدد من الفئة وموفر مورد cmdlet DSC هي العناصر التالية:
- ملف تنسيق كائن الإدارة (MOF) غير مطلوب.
- المجلد الفرعي DSCResource في مجلد الوحدة النمطية غير مطلوب.
- يمكن أن يحتوي ملف الوحدة النمطية PowerShell على فئات موارد DSC متعددة.
إنشاء موفر موارد DSC محدد من الفئة
المثال التالي هو موفر موارد DSC المعرفة من الفئة التي يتم حفظها كوحدة نمطية، MyDSCResource.psm1. يجب عليك دائما تضمين خاصية مفتاح في موفر موارد DSC محدد من الفئة.
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
}
}
إنشاء بيان وحدة نمطية
بعد إنشاء موفر موارد DSC المعرف من الفئة وحفظه كوحدة نمطية، قم بإنشاء بيان وحدة نمطية للوحدة النمطية. لتوفير مورد يستند إلى الفئة لمحرك DSC، يجب تضمين عبارة DscResourcesToExport
في ملف البيان الذي يوجه الوحدة النمطية لتصدير المورد. في هذا المثال، يتم حفظ بيان الوحدة النمطية التالي ك 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 = ''
}
توزيع موفر موارد DSC
انشر موفر موارد DSC الجديد عن طريق إنشاء مجلد MyDscResource في $pshome\Modules
أو $env:SystemDrive\ProgramFiles\WindowsPowerShell\Modules
.
لا تحتاج إلى إنشاء مجلد فرعي DSCResource. انسخ ملفات بيان الوحدة النمطية والوحدة النمطية (MyDscResource.psm1 وMyDscResource.psd1) إلى مجلد MyDscResource.
من هذه النقطة، يمكنك إنشاء وتشغيل برنامج نصي للتكوين كما تفعل مع أي مورد DSC.
إنشاء برنامج نصي لتكوين DSC
بعد حفظ ملفات الفئة والبيانات في بنية المجلد كما هو موضح سابقا، يمكنك إنشاء تكوين يستخدم المورد الجديد. يشير التكوين التالي إلى الوحدة النمطية MyDSCResource. احفظ التكوين كبرنامج نصي، MyResource.ps1.
للحصول على معلومات حول كيفية تشغيل تكوين DSC، راجع نظرة عامة على تكوين الحالة المطلوبة ل Windows PowerShell.
قبل تشغيل التكوين، قم بإنشاء C:\test.txt
. يتحقق التكوين من وجود الملف في c:\test\test.txt
. إذا لم يكن الملف موجودا، ينسخ التكوين الملف من 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
قم بتشغيل هذا البرنامج النصي كما تفعل مع أي برنامج نصي لتكوين DSC. لبدء التكوين، في وحدة تحكم PowerShell غير مقيدة، قم بتشغيل ما يلي:
PS C:\test> .\MyResource.ps1
التوريث في فئات PowerShell
الإعلان عن الفئات الأساسية لفئات PowerShell
يمكنك الإعلان عن فئة PowerShell كنوع أساسي لفئة PowerShell أخرى، كما هو موضح في المثال التالي، حيث الفاكهة هو نوع أساسي apple.
class fruit
{
[int]sold() {return 100500}
}
class apple : fruit {}
[apple]::new().sold() # return 100500
الإعلان عن الواجهات المنفذة لفئات PowerShell
يمكنك تعريف الواجهات المنفذة بعد الأنواع الأساسية، أو مباشرة بعد علامة النقطتين (:
) إذا لم يتم تحديد نوع أساسي. افصل كافة أسماء الأنواع باستخدام الفواصل. يشبه هذا بناء جملة C#.
class MyComparable : system.IComparable
{
[int] CompareTo([object] $obj)
{
return 0;
}
}
class MyComparableTest : test, system.IComparable
{
[int] CompareTo([object] $obj)
{
return 0;
}
}
استدعاء منشئات الفئة الأساسية
لاستدعاء منشئ فئة أساسية من فئة فرعية، أضف الكلمة الأساسية base
، كما هو موضح في المثال التالي:
class A {
[int]$a
A([int]$a)
{
$this.a = $a
}
}
class B : A
{
B() : base(103) {}
}
[B]::new().a # return 103
إذا كانت الفئة الأساسية تحتوي على منشئ افتراضي (لا توجد معلمات)، يمكنك حذف استدعاء منشئ صريح، كما هو موضح.
class C : B
{
C([int]$c) {}
}
استدعاء أساليب الفئة الأساسية
يمكنك تجاوز الأساليب الموجودة في الفئات الفرعية. للقيام بالتجاوز، قم بتعريف الأساليب باستخدام نفس الاسم والتوقيع.
class baseClass
{
[int]days() {return 100500}
}
class childClass1 : baseClass
{
[int]days () {return 200600}
}
[childClass1]::new().days() # return 200600
لاستدعاء أساليب الفئة الأساسية من التطبيقات التي تم تجاوزها، يتم تحويلها إلى الفئة الأساسية ([baseclass]$this)
على الاستدعاء.
class childClass2 : baseClass
{
[int]days()
{
return 3 * ([baseClass]$this).days()
}
}
[childClass2]::new().days() # return 301500
جميع أساليب PowerShell ظاهرية. يمكنك إخفاء أساليب .NET غير الظاهرية في فئة فرعية باستخدام نفس بناء الجملة كما تفعل لتجاوز: تعريف الأساليب بنفس الاسم والتوقيع.
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
القيود الحالية مع توريث الفئة
القيد مع توريث الفئة هو أنه لا يوجد بناء جملة للإعلان عن الواجهات في PowerShell.
تعريف الأنواع المخصصة في PowerShell
قدم Windows PowerShell 5.0 العديد من عناصر اللغة.
الكلمة الأساسية للفئة
تعريف فئة جديدة.
الكلمة الأساسية class
هي نوع .NET Framework صحيح.
أعضاء الفئة عامون.
class MyClass
{
}
تعداد الكلمة الأساسية والتعدادات
تمت إضافة دعم الكلمة الأساسية enum
وهو تغيير فاصل. محدد enum
هو حاليا سطر جديد. الحل البديل لأولئك الذين يستخدمون بالفعل enum
هو إدراج علامة العطف (&
) قبل الكلمة. القيود الحالية: لا يمكنك تعريف قائمة تعداد من حيث نفسه، ولكن يمكنك تهيئة enum
من حيث enum
آخر ، كما هو موضح في المثال التالي:
لا يمكن تحديد النوع الأساسي حاليا. النوع الأساسي هو دائما [int].
enum Color2
{
Yellow = [Color]::Blue
}
يجب أن تكون قيمة قائمة التعداد ثابت وقت تحليل. لا يمكن تعيين قيمة قائمة التعداد إلى نتيجة أمر تم استدعاؤه.
enum MyEnum
{
Enum1
Enum2
Enum3 = 42
Enum4 = [int]::MaxValue
}
يدعم Enum
العمليات الحسابية، كما هو موضح في المثال التالي:
enum SomeEnum { Max = 42 }
enum OtherEnum { Max = [SomeEnum]::Max + 1 }
الكلمة الأساسية المخفية
الكلمة الأساسية hidden
، المقدمة في Windows PowerShell 5.0، تخفي أعضاء الفئة عن نتائج Get-Member
الافتراضية. حدد الخاصية المخفية كما هو موضح في السطر التالي:
hidden [type] $classmember = <value>
لا يتم عرض الأعضاء المخفية باستخدام إكمال علامة التبويب أو IntelliSense، ما لم يحدث الإكمال في الفئة التي تعرف العضو المخفي.
تمت إضافة سمة جديدة، System.Management.Automation.HiddenAttribute، بحيث يمكن أن يكون للتعليمات البرمجية C# نفس الدلالات داخل PowerShell.
لمزيد من المعلومات، راجع [about_Hidden[(/powershell/module/microsoft.powershell.core/about/about_hidden).
Import-DscResource
Import-DscResource
الآن كلمة أساسية ديناميكية حقيقية. يقوم PowerShell بتحليل الوحدة النمطية الجذر للوحدة النمطية المحددة، والبحث عن الفئات التي تحتوي على سمة DscResource.
خصائص
تمت إضافة حقل جديد، ImplementingAssembly
، إلى ModuleInfo
. إذا كان البرنامج النصي يعرف الفئات، أو تم تعيين التجميع المحمل للوحدات الثنائية ImplementingAssembly
إلى التجميع الديناميكي الذي تم إنشاؤه لوحدة البرنامج النصي. لا يتم تعيينه عند ModuleType = Manifest.
يكتشف الانعكاس على حقل ImplementingAssembly
الموارد في وحدة نمطية. وهذا يعني أنه يمكنك اكتشاف الموارد المكتوبة إما ب PowerShell أو باللغات المدارة الأخرى.
الحقول ذات المهايئات.
[int] $i = 5
ثابت مدعوم ويعمل كسمة، على غرار قيود النوع، بحيث يمكن تحديده بأي ترتيب.
static [int] $count = 0
النوع اختياري.
$s = "hello"
جميع الأعضاء عامون. تتطلب الخصائص إما سطرا جديدا أو فاصلة منقوطة. إذا لم يتم تحديد نوع كائن، يكون نوع الخاصية الكائن .
الدالات الإنشائية وإنشاء مثيل
يمكن أن تحتوي فئات PowerShell على منشئات لها نفس اسم فئتها. يمكن تحميل الدالات الإنشائية بشكل زائد. يتم دعم الدالات الإنشائية الثابتة.
تتم تهيئة الخصائص ذات تعبيرات التهيئة قبل تشغيل أي تعليمة برمجية في الدالة الإنشائية. تتم تهيئة الخصائص الثابتة قبل نص الدالة الإنشائية الثابتة، وتتم تهيئة خصائص المثيل قبل نص الدالة الإنشائية غير الثابتة. حاليا، لا يوجد بناء جملة لاستدعاء منشئ من منشئ آخر مثل بناء جملة C#: ": this()")
. الحل البديل هو تعريف أسلوب Init شائع.
فيما يلي طرق إنشاء مثيل للفئات:
إنشاء مثيل باستخدام الدالة الإنشائية الافتراضية. لاحظ أن
New-Object
غير معتمد في هذا الإصدار.$a = [MyClass]::new()
استدعاء الدالة الإنشائية مع معلمة.
$b = [MyClass]::new(42)
تمرير صفيف إلى منشئ بمعلمات متعددة
$c = [MyClass]::new(@(42,43,44), "Hello")
بالنسبة لهذا الإصدار، يكون اسم النوع مرئيا فقط معجميا، مما يعني أنه غير مرئي خارج الوحدة النمطية أو البرنامج النصي الذي يعرف الفئة. يمكن أن ترجع الدالات مثيلات فئة محددة في PowerShell، وتعمل المثيلات بشكل جيد خارج الوحدة النمطية أو البرنامج النصي.
تسرد المعلمة Get-Member
Static الدالات الإنشائية، بحيث يمكنك عرض التحميلات الزائدة مثل أي أسلوب آخر. كما أن أداء بناء الجملة هذا أسرع بكثير من New-Object
.
تعمل الطريقة الثابتة الزائفة المسماة الجديدة مع أنواع .NET، كما هو موضح في المثال التالي. [hashtable]::new()
يمكنك الآن رؤية التحميل الزائد للمنشئ مع Get-Member
، أو كما هو موضح في هذا المثال:
[hashtable]::new
OverloadDefinitions
-------------------
hashtable new()
hashtable new(int capacity)
hashtable new(int capacity, float loadFactor)
أساليب
يتم تنفيذ أسلوب فئة PowerShell ك ScriptBlock يحتوي على كتلة نهاية فقط. جميع الأساليب عامة. يوضح التالي مثالا لتعريف أسلوب يسمى DoSomething.
class MyClass
{
DoSomething($x)
{
$this._doSomething($x) # method syntax
}
private _doSomething($a) {}
}
استدعاء الأسلوب
يتم دعم الأساليب ذات التحميل الزائد. تتم تسمية الأساليب ذات التحميل الزائد بنفس أسلوب موجود ولكن يتم تمييزها بقيمها المحددة.
$b = [MyClass]::new()
$b.DoSomething(42)
دعاء
راجعاستدعاء أسلوب
سمات
تمت إضافة ثلاث سمات جديدة: DscResource
DscResourceKey
DscResourceMandatory
.
أنواع الإعادة
نوع المرجع هو عقد. يتم تحويل القيمة المرجعة إلى النوع المتوقع. إذا لم يتم تحديد نوع الإرجاع، يكون نوع الإرجاع فارغا. لا يوجد تدفق للكائنات ولا يمكن كتابة الكائنات إلى البنية الأساسية لبرنامج ربط العمليات التجارية إما عن قصد أو عن طريق الصدفة.
النطاق المعجمي للمتغيرات
يوضح ما يلي مثالا على كيفية عمل النطاق المعجمي في هذا الإصدار.
$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
مثال: إنشاء فئات مخصصة
ينشئ المثال التالي العديد من الفئات المخصصة الجديدة لتنفيذ لغة ورقة أنماط ديناميكية HTML (DSL). يضيف المثال دالات المساعد لإنشاء أنواع عناصر معينة كجزء من فئة العنصر، مثل أنماط العناوين والجداول، لأنه لا يمكن استخدام الأنواع خارج نطاق الوحدة النمطية.
# 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 }