我想在PowerShell中使用事件来分离职责并消除模块之间的依赖关系。子模块需要向父模块通知其活动,有时需要在父模块引发事件时返回信息。
这是我到目前为止所尝试的。在实际场景中,我会将注册,注销和处理函数部分放在父模块中,将New-Event部分放在子模块中。
function MyHandler {
param (
[System.Management.Automation.PSEventArgs]$Event
)
Write-Host "In MyHandler now!" -ForegroundColor Yellow
Write-Host "Sender : $($Event.Sender)"
Write-Host "SourceArgs : $($Event.SourceArgs)"
Write-Host "MessageData : $($Event.MessageData)"
Write-Host "TimeGenerated : $($Event.TimeGenerated)"
$Event.MessageData = "This does not make it back."
return "Neither does this."
}
# subscribe
[String]$MessageData = "Input"
Register-EngineEvent -SourceIdentifier Pipo -Action {MyHandler $Event }
# raise event and see what we get back
New-Event -Sender "Me" -SourceIdentifier "Pipo" -EventArguments "MyEventArgs" -MessageData $MessageData
# cleanup
Unregister-Event -SourceIdentifier Pipo
这会触发对MyHandler的调用,我可以访问传递的参数和事件属性。问题是我无法找到一种方法将数据从事件处理函数返回到事件的提升者。我的思维方式可能太过C#导向,我可能会尝试以一种我应该在PS中采用不同方式的方式来做事。创建一个对象并将其发送到管道以便引发事件的整个概念对我来说似乎很奇怪,我不明白究竟发生了什么。
这样做的正确方法是什么?
答案 0 :(得分:0)
我找到了满足我需求的方法。事实证明我在搜索错误的关键字,在搜索PowerShell和回调时,我发现这个问题对我有很大帮助(特别是Duncan的回答):
Pass a function as a parameter in PowerShell
我把它作为一个完整的例子。这是一个客户端脚本,保存为“Client.ps1”:
Import-Module -Name ".\Server.psm1" -DisableNameChecking
$script:ClientVar = "Not seen by server, returned by event handler."
function Handle_Initialized {
Write-Host "Handler Initialized is being called."
# a return value is optional
return "=== $script:ClientVar ==="
}
function Handle_ProcessedData {
param (
$Argument1,
$Argument2,
$Argument3
)
Write-Host "Handler ProcessedData is called."
Write-Host "Arguments are $argument1, $argument2 and $argument3."
# a return value is optional
return "=== $argument1, $argument2, $argument3 ==="
}
Subscribe-Event -Name Initialized -Handler $function:Handle_Initialized
Subscribe-Event -Name ProcessedData -Handler $function:Handle_ProcessedData
Write-Host ""
Write-Host "calling with active subscriptions"
Write-Host "================================="
Do-ServerStuff
Unsubscribe-Event -Name Initialized
Unsubscribe-Event -Name ProcessedData
Write-Host ""
Write-Host "calling again with no active subscriptions"
Write-Host "================================="
Do-ServerStuff
Remove-Module -Name "Server"
然后,在同一个文件夹中,将其设置为“Server.psm1”:
[ScriptBlock]$script:Handler_Initialized = $null
[ScriptBlock]$script:Handler_ProcessedData = $null
function Subscribe-Event {
param (
[String]$Name,
[ScriptBlock]$Handler
)
switch ($Name) {
Initialized { $script:Handler_Initialized = $Handler }
ProcessedData { $script:Handler_ProcessedData = $Handler }
}
}
function Unsubscribe-Event {
param (
[String]$Name
)
switch ($Name) {
Initialized { $script:Handler_Initialized = $null }
ProcessedData { $script:Handler_ProcessedData = $null }
}
}
function Raise-Initialized {
param (
)
if ($script:Handler_Initialized) {
return & $script:Handler_Initialized
}
}
function Raise-ProcessedData {
param (
[Object]$Argument1,
[Object]$Argument2,
[Object]$Argument3
)
if ($script:Handler_ProcessedData) {
return & $script:Handler_ProcessedData -Argument1 $Argument1 -Argument2 $Argument2 -Argument3 $Argument3
}
}
function Do-ServerStuff {
Write-Host "Before raising event Initialized."
Raise-Initialized
Write-Host "After raising event Initialized."
Write-Host ""
Write-Host "Before raising event ProcessedData."
Raise-ProcessedData -Argument1 "AAA" -Argument2 "BBB" -Argument3 "CCC"
Write-Host "After raising event ProcessedData."
}
你会发现你有一个可扩展的同步事件处理系统。
大多数管道都在服务器模块中,它决定了处理程序函数的原型。客户端代码仅订阅和取消订阅事件并提供处理程序实现。它支持所有处理程序的命名参数以及以通常的PowerShell方式返回值(实际输出值)。服务器对我喜欢的任何客户一无所知,依赖只有一种方式。
例如,此方案允许您构建执行纯核心逻辑的模块,而不是其他任何模块。您可以让服务器模块引发一个发送消息的事件,然后客户端可以决定如何处理它以及在何处发送消息,而不是记录某些全局对象。这增加了服务器模块的可用性并使其可测试。
这可能是一个品味问题,我通常倾向于使用事件而不是依赖注入。通过依赖注入,服务器仍然需要知道注入的类型,事件并非如此。
享受!