about_Classes_and_DSC

وصف قصير

توضح هذه المقالة كيفية استخدام الفئات للتطوير في PowerShell باستخدام تكوين الحالة المطلوبة (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.

أنشئ برنامج نصي لتكوين الحالة المرغوبة

بعد حفظ ملفات الفئة والبيانات في بنية المجلد كما هو موضح سابقا، يمكنك إنشاء تكوين يستخدم المورد الجديد. يشير التكوين التالي إلى الوحدة النمطية 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 أخرى، كما هو موضح في المثال التالي، حيث تكون الفاكهة نوعا أساسيا للتفاح.

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"

جميع الأعضاء عامون. تتطلب الخصائص إما خطا جديدا أو فاصلة منقوطة. إذا لم يتم تحديد نوع كائن، فإن نوع الخاصية هو Object.

الدالات الإنشائية وإنشاء مثيل

يمكن أن تحتوي فئات PowerShell على منشئات لها نفس اسم فئتها. يمكن تحميل الدالات الإنشائية بشكل زائد. يتم دعم المنشئات الثابتة. تتم تهيئة الخصائص ذات تعبيرات التهيئة قبل تشغيل أي تعليمة برمجية في الدالة الإنشائية. تتم تهيئة الخصائص الثابتة قبل نص الدالة الإنشائية الثابتة، وتتم تهيئة خصائص المثيل قبل نص الدالة الإنشائية غير الثابتة. حاليا، لا يوجد بناء جملة لاستدعاء منشئ من منشئ آخر مثل بناء جملة C#: ": this()"). الحل البديل هو تعريف أسلوب Init شائع.

فيما يلي طرق إنشاء مثيل للفئات:

  • إنشاء مثيل باستخدام الدالة الإنشائية الافتراضية. لاحظ أن New-Object غير معتمد في هذا الإصدار.

    $a = [MyClass]::new()

  • استدعاء منشئ مع معلمة.

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

  • تمرير صفيف إلى منشئ بمعلمات متعددة

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

بالنسبة لهذا الإصدار، يكون اسم النوع مرئيا فقط معجميا، مما يعني أنه غير مرئي خارج الوحدة النمطية أو البرنامج النصي الذي يعرف الفئة. يمكن أن ترجع الدالات مثيلات فئة معرفة في PowerShell، وتعمل المثيلات بشكل جيد خارج الوحدة النمطية أو البرنامج النصي.

تسرد Get-Member المعلمة الثابتة الدالات الإنشائية، بحيث يمكنك عرض التحميلات الزائدة مثل أي أسلوب آخر. أداء بناء الجملة هذا هو أيضا أسرع بكثير من New-Object.

يعمل الأسلوب الزائف الثابت المسمى new مع أنواع .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 }

راجع أيضًا

about_Enum

about_Hidden

about_Language_Keywords

about_Methods

إنشاء موارد تكوين الحالة المطلوبة ل PowerShell المخصصة