【问题标题】:Can I write a class using PowerShell?我可以使用 PowerShell 编写课程吗?
【发布时间】:2021-06-18 21:33:00
【问题描述】:

由于 PowerShell 构建在 .NET 框架之上,我可以使用 PowerShell 编写自己的自定义类吗?

我不是在谈论实例化 .NET 类……那部分很简单。我想使用 PowerShell 脚本编写自己的自定义类。可能吗?到目前为止,我的研究表明这是不可能的。

我还在学习 PowerShell,尽管搜索了几次,但到目前为止我还没有在这个网站上找到答案。

【问题讨论】:

    标签: powershell


    【解决方案1】:

    查看Add-Type cmdlet。它允许您在 PowerShell 中编写 C# 和其他代码。 例如(来自上面的链接),在 PowerShell 窗口中,

    $source = @"
    public class BasicTest
    {
        public static int Add(int a, int b)
        {
            return (a + b);
        }
    
        public int Multiply(int a, int b)
        {
            return (a * b);
        }
    }
    "@
    
    Add-Type -TypeDefinition $source
    
    [BasicTest]::Add(4, 3)
    
    $basicTestObject = New-Object BasicTest
    $basicTestObject.Multiply(5, 2)
    

    【讨论】:

    • 抱歉,我理解错了。我以为你想要类的功能,但完全在 PS 中完成。是的,如果您想编写一个 .net 类并在 PS 中使用它,那么 Add-Type 是您的最佳选择。我已经更新了我的答案以匹配此处的示例,但纯粹是在 PS 中,以防万一有人在寻找它。唯一的区别是我将乘法函数设为私有,并将超秘密函数作为乘法接口包含在内。
    • 这个答案在 PowerShell 5.0 中已经过时了,它引入了 class 关键字。请参阅下面的@Rubanov 答案。
    【解决方案2】:

    我怀疑您正在寻找的解决方案是PowerShell Modules。它们执行类通常在其他语言中执行的角色。它们为您提供了一种非常简单但结构化的方式来重用您的代码。

    这里是如何使用模块在 PowerShell 中获取类的功能。在命令行你可以这样做:

    New-Module -ScriptBlock {function add($a,$b){return $a + $b}; function multiply($a,$b){return $a * $b}; function supersecret($a,$b){return multiply $a $b}; export-modulemember -function add, supersecret}
    

    那么你就可以:

    PS C:\> add 2 4
    6
    PS C:\> multiply 2 4
    The term 'multiply' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
    At line:1 char:9
    + multiply <<<<  2 4
        + CategoryInfo          : ObjectNotFound: (multiply:String) [], CommandNotFoundException
        + FullyQualifiedErrorId : CommandNotFoundException
    
    PS C:\> supersecret 2 4
    8
    

    正如你所见,multiply 在模块中是私有的。更传统地,您将实例化一个作为模块实例的对象。这是通过 -AsCustomObject 参数完成的:

    $m = New-Module -ScriptBlock {function add($a,$b){return $a + $b}; function multiply($a,$b){return $a * $b}; function supersecret($a,$b){return multiply $a $b}; export-modulemember -function add, supersecret} -AsCustomObject
    

    那么你可以:

    PS C:\> $m.add(2,4)
    6
    PS C:\> $m.multiply(2,4)
    Method invocation failed because [System.Management.Automation.PSCustomObject] doesn't contain a method named 'multiply'.
    At line:1 char:12
    + $m.multiply <<<< (2,4)
        + CategoryInfo          : InvalidOperation: (multiply:String) [], RuntimeException
        + FullyQualifiedErrorId : MethodNotFound
    
    PS C:\> $m.supersecret(2,4)
    
    8
    

    这一切都展示了 dynamic 模块的使用,这意味着没有任何东西存储到磁盘以供重复使用。它适用于非常简单的功能。但是,如果您希望真正能够阅读代码并在以后的会话或脚本中重用它,您可能希望将其存储在 .psm1 文件中,然后将该文件存储在具有相同名称的文件夹中(减去扩展名)作为文件。然后,您可以在命令行或其他脚本中将模块导入会话。

    作为一个例子,假设我使用了这段代码:

    function Add{
        param(
                $a,
                $b
             )
        return $a + $b
    }
    
    function Multiply{
        param(
                $a,
                $b
             )
        return $a + $b
    }
    
    function SuperSecret{
        param(
                $a,
                $b
             )
        return Multiply $a $b
    }
    Export-ModuleMember -Function Add, SuperSecret
    

    并将其保存到文件夹中名为 TestModule.psm1 的文件中:C:\Windows\System32\WindowsPowerShell\v1.0\Modules\TestModule

    PowerShell 安装文件夹中的 Modules 文件夹是一个神奇的文件夹,其中存储的任何模块都对 Import-Module cmdlet 可见,而无需指定路径。现在,如果我们在命令行中运行Get-Module -List,我们会看到:

    ModuleType Name                      ExportedCommands
    ---------- ----                      ----------------
    Script     DotNet                    {}
    Manifest   FileSystem                {Get-FreeDiskSpace, New-Zip, Resolve-ShortcutFile, Mount-SpecialFolder...}
    Manifest   IsePack                   {Push-CurrentFileLocation, Select-CurrentTextAsVariable, ConvertTo-Short...
    Manifest   PowerShellPack            {New-ByteAnimationUsingKeyFrames, New-TiffBitmapEncoder, New-Viewbox, Ne...
    Manifest   PSCodeGen                 {New-Enum, New-ScriptCmdlet, New-PInvoke}
    Manifest   PSImageTools              {Add-CropFilter, Add-RotateFlipFilter, Add-OverlayFilter, Set-ImageFilte...
    Manifest   PSRss                     {Read-Article, New-Feed, Remove-Article, Remove-Feed...}
    Manifest   PSSystemTools             {Test-32Bit, Get-USB, Get-OSVersion, Get-MultiTouchMaximum...}
    Manifest   PSUserTools               {Start-ProcessAsAdministrator, Get-CurrentUser, Test-IsAdministrator, Ge...
    Manifest   TaskScheduler             {Remove-Task, Get-ScheduledTask, Stop-Task, Add-TaskTrigger...}
    Manifest   WPK                       {Get-DependencyProperty, New-ModelVisual3D, New-DiscreteVector3DKeyFrame...
    Manifest   AppLocker                 {}
    Manifest   BitsTransfer              {}
    Manifest   PSDiagnostics             {}
    Script     **TestModule**            {}
    Manifest   TroubleshootingPack       {}
    Manifest   Citrix.XenApp.Commands... {}
    

    我们可以看到我们的模块已准备好导入。我们可以将其导入会话并在原始使用中使用它:

    Import-Module TestModule
    

    或者我们可以再次实例化一个对象:

    $m = Import-Module TestModule -AsCustomObject
    

    【讨论】:

    • 所以看起来答案是否定的。即使在模块中,我也看不到任何有关编写自定义类的文档。我想我必须使用 C# 来编写自定义类。
    • 我想我不明白你所说的类是什么意思?你想让班级做什么?
    • 我可以用 C# 编写一个具有公共和私有成员变量和方法的类。到目前为止,powershell 似乎还没有这样做。
    • 来自链接:“模块可用于打包和分发用于执行常见任务的内聚函数库。通常,这些函数的名称共享一个或多个名词来反映它们的常见任务用于。这些函数也可以类似于 .NET Framework 类,因为它们可以有公共和私有成员。"
    【解决方案3】:

    您可以使用PowerShell 5.0中引入的the class keyword

    这是example by Trevor Sullivan。 (存档here。)

    ##################################################
    ####### WMF 5.0 November 2014 Preview ###########
    ##################################################
    class Beer {
        # Property: Holds the current size of the beer.
        [Uint32] $Size;
        # Property: Holds the name of the beer's owner.
        [String] $Name;
    
        # Constructor: Creates a new Beer object, with the specified
        #              size and name / owner.
        Beer([UInt32] $NewSize, [String] $NewName) {
            # Set the Beer size
            $this.Size = $NewSize;
            # Set the Beer name
            $this.Name = $NewName;
        }
    
        # Method: Drink the specified amount of beer.
        # Parameter: $Amount = The amount of beer to drink, as an 
        #            unsigned 32-bit integer.
        [void] Drink([UInt32] $Amount) {
            try {
                $this.Size = $this.Size - $Amount;
            }
            catch {
                Write-Warning -Message 'You tried to drink more beer than was available!';
            }
        }
    
        # Method: BreakGlass resets the beer size to 0.
        [void] BreakGlass() {
            Write-Warning -Message 'The beer glass has been broken. Resetting size to 0.';
            $this.Size = 0;
        }
    }
    

    这适用于 Windows 10 专业版。

    像这样试驾:

    # Create a new 33 centilitre beer, named 'Chimay'
    $chimay = [Beer]::new(33, 'Chimay');
    
    $chimay.Drink(10)
    $chimay.Drink(10)
    
    # Need more beer!
    $chimay.Drink(200)
    
    $chimay.BreakGlass()
    

    【讨论】:

    • 我发现 (here) 你可以通过强制转换哈希表来初始化:$chimay = [Beer]@{ Size=33; Name='Chimay'}。有点像 C# 的对象初始化器语法。但是,如果构造函数有参数,则会引发错误。
    猜你喜欢
    • 2017-02-15
    • 2022-01-26
    • 2012-02-08
    • 2013-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多