适用于 PowerShell 5.1 及以上版本
在 PowerShell 的早期版本中,我们通常使用 PSCustomObject 或哈希表来构建自定义数据结构。虽然它们足够灵活,但缺乏类型约束、无法定义方法、也不支持继承,在构建大型自动化项目时显得力不从心。PowerShell 5.0 引入了 class 关键字,让我们可以直接在脚本中定义真正的 .NET 类型。
class 不仅仅是语法糖,它带来了完整的面向对象编程能力:类型安全的属性、可重载的构造函数、继承与多态、以及与 .NET 生态的无缝集成。你可以用 class 来建模业务实体、封装复杂逻辑、甚至实现设计模式,让脚本从”一次性工具”进化为可维护的工程化代码。
本文将从基础类定义开始,逐步深入继承与多态,最后通过一个服务器管理框架的实战案例,展示 class 在真实自动化场景中的威力。
基础类定义 下面通过一个 ServerInfo 类来演示属性、构造函数和方法的定义与使用。这个类用于封装服务器的基本信息,并提供格式化输出和状态检查方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 class ServerInfo { [string ]$Name [string ]$IPAddress [int ]$Port [string ]$Environment [datetime ]$LastChecked ServerInfo() { $this .Port = 443 $this .Environment = 'Development' $this .LastChecked = Get-Date } ServerInfo([string ]$Name , [string ]$IPAddress , [int ]$Port ) { $this .Name = $Name $this .IPAddress = $IPAddress $this .Port = $Port $this .Environment = 'Production' $this .LastChecked = Get-Date } [string ] GetEndpoint () { return "$ ($this .IPAddress):$ ($this .Port)" } [bool ] TestConnection () { try { $tcp = [System.Net.Sockets.TcpClient ]::new() $connect = $tcp .BeginConnect($this .IPAddress, $this .Port, $null , $null ) $wait = $connect .AsyncWaitHandle.WaitOne(3000 , $false ) if ($wait ) { $tcp .EndConnect($connect ) $this .LastChecked = Get-Date return $true } return $false } finally { $tcp .Dispose() } } [string ] ToString () { return "[$ ($this .Environment)] $ ($this .Name) ($ ($this .GetEndpoint()))" } } $devServer = [ServerInfo ]::new()$devServer .Name = 'DEV-WEB-01' $devServer .IPAddress = '192.168.1.100' Write-Host $devServer .ToString()$prodServer = [ServerInfo ]::new('PROD-WEB-01' , '10.0.0.50' , 8443 )Write-Host "服务器: $ ($prodServer .Name)" Write-Host "端点: $ ($prodServer .GetEndpoint())" Write-Host "环境: $ ($prodServer .Environment)" Write-Host "上次检查: $ ($prodServer .LastChecked.ToString('yyyy-MM-dd HH:mm:ss'))"
执行结果示例:
1 2 3 4 5 [Development] DEV-WEB-01 (192.168.1.100:443) 服务器: PROD-WEB-01 端点: 10.0.0.50:8443 环境: Production 上次检查: 2026-01-05 08:30:15
继承与多态 当类之间具有层次关系时,继承可以让子类复用父类的属性和方法,同时添加或重写自己的行为。下面的例子定义了一个通用的部署任务基类,然后派生出两种具体的任务类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 class DeploymentTask { [string ]$TaskName [string ]$TargetServer [datetime ]$StartTime [datetime ]$EndTime [string ]$Status = 'Pending' DeploymentTask([string ]$TaskName , [string ]$TargetServer ) { $this .TaskName = $TaskName $this .TargetServer = $TargetServer } [void ] Execute () { $this .StartTime = Get-Date Write-Host "正在执行任务: $ ($this .TaskName)" } [void ] Complete () { $this .EndTime = Get-Date $this .Status = 'Completed' $duration = ($this .EndTime - $this .StartTime).TotalSeconds Write-Host "任务完成: $ ($this .TaskName) (耗时 $ ([math]::Round($duration , 2)) 秒)" } [void ] Fail ([string ]$Reason ) { $this .EndTime = Get-Date $this .Status = "Failed: $Reason " Write-Host "任务失败: $ ($this .TaskName) - $Reason " } [hashtable ] GetSummary () { return @ { TaskName = $this .TaskName TargetServer = $this .TargetServer Status = $this .Status Duration = if ($this .StartTime -and $this .EndTime) { ($this .EndTime - $this .StartTime).TotalSeconds } else { 0 } } } } class WebAppDeployment : DeploymentTask { [string ]$AppPoolName [string ]$SiteName [string ]$PackagePath WebAppDeployment( [string ]$TargetServer , [string ]$SiteName , [string ]$PackagePath ) : base("Deploy-WebApp-$SiteName " , $TargetServer ) { $this .SiteName = $SiteName $this .AppPoolName = "$SiteName -Pool" $this .PackagePath = $PackagePath } [void ] Execute () { ([DeploymentTask ]$this ).Execute() Write-Host " 停止应用池: $ ($this .AppPoolName)" Write-Host " 备份当前版本..." Write-Host " 解压部署包: $ ($this .PackagePath)" Write-Host " 配置站点: $ ($this .SiteName)" Write-Host " 启动应用池: $ ($this .AppPoolName)" $this .Complete() } } class DatabaseMigration : DeploymentTask { [string ]$DatabaseName [string ]$MigrationScript [bool ]$BackupBeforeMigration = $true DatabaseMigration( [string ]$TargetServer , [string ]$DatabaseName , [string ]$MigrationScript ) : base("DB-Migration-$DatabaseName " , $TargetServer ) { $this .DatabaseName = $DatabaseName $this .MigrationScript = $MigrationScript } [void ] Execute () { ([DeploymentTask ]$this ).Execute() Write-Host " 目标数据库: $ ($this .DatabaseName)" if ($this .BackupBeforeMigration) { Write-Host " 正在备份数据库..." } Write-Host " 执行迁移脚本: $ ($this .MigrationScript)" Write-Host " 验证迁移结果..." $this .Complete() } } $tasks = @ ( [WebAppDeployment ]::new('WEB-SVR-01' , 'CustomerPortal' , 'D:\Packages\v2.5.0.zip' ) [DatabaseMigration ]::new('DB-SVR-01' , 'CustomerDB' , 'v2.5.0_schema_update.sql' ) ) foreach ($task in $tasks ) { Write-Host "`n--- 部署任务 ---" $task .Execute() $summary = $task .GetSummary() Write-Host "摘要: $ ($summary .Status)" }
执行结果示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 --- 部署任务 --- 正在执行任务 : Deploy-WebApp-CustomerPortal 停止应用池 : CustomerPortal-Pool 备份当前版本... 解压部署包 : D:\Packages\v2.5.0.zip 配置站点 : CustomerPortal 启动应用池 : CustomerPortal-Pool 任务完成 : Deploy-WebApp-CustomerPortal (耗时 12.35 秒) 摘要 : Completed --- 部署任务 --- 正在执行任务 : DB-Migration-CustomerDB 目标数据库 : CustomerDB 正在备份数据库... 执行迁移脚本 : v2.5.0_schema_update.sql 验证迁移结果... 任务完成 : DB-Migration-CustomerDB (耗时 8.72 秒) 摘要 : Completed
实战应用:服务器管理框架 在实际运维场景中,我们可以用 class 构建一个完整的服务器管理框架,包含数据验证、JSON 序列化和结构化错误处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 class ValidateRangeAttribute : System .Management .Automation .ValidateArgumentsAttribute { [int ]$MinValue [int ]$MaxValue ValidateRangeAttribute([int ]$Min , [int ]$Max ) { $this .MinValue = $Min $this .MaxValue = $Max } [void ] Validate ([object ]$arguments , [System.Management.Automation.EngineIntrinsics ]$engineIntrinsics ) { $val = [int ]$arguments if ($val -lt $this .MinValue -or $val -gt $this .MaxValue) { throw "值 $val 不在允许范围 ($ ($this .MinValue) - $ ($this .MaxValue)) 内" } } } class ManagedServer { [ValidateNotNullOrEmpty ()][string ]$HostName [string ]$IPAddress [string ]$Role [string ]$DataCenter [bool ]$IsMonitored = $false static [System.Collections.Generic.List [ManagedServer ]]$Registry = [System.Collections.Generic.List [ManagedServer ]]::new() ManagedServer([string ]$HostName , [string ]$IPAddress ) { $this .HostName = $HostName $this .IPAddress = $IPAddress [ManagedServer ]::Registry.Add($this ) } [string ] ToJson () { $ordered = [ordered ]@ { hostName = $this .HostName ipAddress = $this .IPAddress role = $this .Role dataCenter = $this .DataCenter isMonitored = $this .IsMonitored } return $ordered | ConvertTo-Json -Depth 3 } static [ManagedServer ] FromJson ([string ]$Json ) { $data = $Json | ConvertFrom-Json $server = [ManagedServer ]::new($data .hostName, $data .ipAddress) $server .Role = $data .role $server .DataCenter = $data .dataCenter $server .IsMonitored = $data .isMonitored return $server } static [array ] GetRegisteredServers () { return [ManagedServer ]::Registry.ToArray() } } class ManagedWebServer : ManagedServer { [string []]$BoundUrls = @ () [int ]$WorkerProcesses = 4 [string ]$RuntimeVersion = '8.0' [hashtable ]$HealthMetrics = @ {} ManagedWebServer([string ]$HostName , [string ]$IPAddress ) : base($HostName , $IPAddress ) { $this .Role = 'WebServer' } [hashtable ] CheckHealth () { $result = @ { Server = $this .HostName Timestamp = Get-Date -Format 'o' Checks = @ () } $checks = @ ( @ { Name = 'CPU' ; Value = (Get-Random -Min 10 -Max 95 ); Unit = '%' } @ { Name = 'Memory' ; Value = (Get-Random -Min 30 -Max 90 ); Unit = '%' } @ { Name = 'Disk' ; Value = (Get-Random -Min 20 -Max 85 ); Unit = '%' } ) foreach ($check in $checks ) { $status = if ($check .Value -gt 80 ) { 'Warning' } else { 'OK' } $result .Checks += @ { Name = $check .Name Value = $check .Value Unit = $check .Unit Status = $status } } $this .HealthMetrics = $result $this .IsMonitored = $true return $result } [string ] ToJson () { $ordered = [ordered ]@ { hostName = $this .HostName ipAddress = $this .IPAddress role = $this .Role dataCenter = $this .DataCenter isMonitored = $this .IsMonitored boundUrls = $this .BoundUrls workerProcesses = $this .WorkerProcesses runtimeVersion = $this .RuntimeVersion } return $ordered | ConvertTo-Json -Depth 3 } } $web1 = [ManagedWebServer ]::new('WEB-PROD-01' , '10.1.0.10' )$web1 .DataCenter = 'EastAsia' $web1 .BoundUrls = @ ('https://portal.contoso.com' , 'https://api.contoso.com' )$web2 = [ManagedWebServer ]::new('WEB-PROD-02' , '10.1.0.11' )$web2 .DataCenter = 'EastAsia' $web2 .BoundUrls = @ ('https://portal.contoso.com' )$web2 .WorkerProcesses = 8 Write-Host "已注册服务器数量: $ ([ManagedServer]::Registry.Count)" Write-Host "`n--- $web1 健康检查 ---" $health = $web1 .CheckHealth()foreach ($check in $health .Checks) { Write-Host (" {0,-10} {1}{2,-5} [{3}]" -f $check .Name, $check .Value, $check .Unit, $check .Status) } Write-Host "`n--- JSON 序列化 ---" Write-Host $web1 .ToJson()
执行结果示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 已注册服务器数量: 2 --- WEB-PROD-01 健康检查 --- CPU 42% [OK] Memory 67% [OK] Disk 31% [OK] --- JSON 序列化 --- { "hostName" : "WEB-PROD-01" , "ipAddress" : "10.1.0.10" , "role" : "WebServer" , "dataCenter" : "EastAsia" , "isMonitored" : true , "boundUrls" : [ "https://portal.contoso.com" , "https://api.contoso.com" ], "workerProcesses" : 4, "runtimeVersion" : "8.0" }
注意事项
PowerShell 版本要求 :class 关键字需要 PowerShell 5.0 及以上版本。在 Windows PowerShell 5.1 中功能完整可用,PowerShell 7 进一步增强了与 .NET Core 的兼容性。如果你需要在旧版本中运行脚本,请改用 PSCustomObject 或 C# 编译的 cmdlet。
属性初始化时机 :类的属性默认值在类定义时求值,不是在实例化时求值。如果需要动态默认值(如当前时间),应放在构造函数中赋值,而不是在属性声明处直接使用 Get-Date。
继承的限制 :PowerShell class 只支持单继承(一个父类),但可以实现多个接口。方法重写时需要使用 ([BaseClass]$this).Method() 语法调用父类方法,这与 C# 的 base.Method() 略有不同。
序列化注意事项 :class 实例默认不能被 Export-Clixml 正确序列化和反序列化,反序列化后会变成 Deserialized.ClassName 对象,丢失方法。如果需要持久化,建议使用 ToJson() / FromJson() 模式。
调试与类型检查 :class 中的方法不支持 Write-Output 返回值(会被忽略),必须使用 return 语句。同时,类方法内的 Write-Verbose 等流输出在某些宿主环境中可能不会显示,建议在方法外部进行日志记录。
性能考量 :虽然 class 提供了强类型和结构化的优势,但对于简单的数据传递场景,PSCustomObject 仍然更轻量。仅在需要封装逻辑、继承关系或类型约束时使用 class,避免过度设计。