每次执行脚本时重新加载 PowerShell 模块

Reload the PowerShell module every time the script is executing

我在 PowerShell 模块文件中有一个 class。 (A.psm1)

class A  {

    [void] printString() {
        write-host 111
    }

}

而且我还有一个使用 class 的简单脚本。

Using module C:\temp\A.psm1

$a = [A]::new()
$a.printString()  # prints 111

但是如果我从 class 更改方法,例如,如此处所示(将 111 替换为 222

[void] printString() {
     write-host 222
}

如果我重新启动脚本,它仍会打印 111。只有当我重新启动 PowerShell 控制台时,它才会打印新值。 如果我只在控制台中工作,我可以使用 Import-Module ... -Force 命令。但它在脚本中不起作用。

那么有没有办法在每次启动脚本时重新加载 PowerShell 模块而不重新启动控制台本身?

据我所知,没有好的解决方案,不幸的是(从 PowerShell 7.2 开始):using module 语句 - 这也是加载 classes 来自模块 - 没有等同于 Import-Module-Force 开关,用于强制 重新加载 模块。

解决方法

# (Force-re)load the module and get a reference to the module.
# This does NOT give you access to the classes defined in the module.
$module = Import-Module C:\temp\A.psm1 -PassThru -Force

# Use the module reference to execute code *in the module's* context,
# where the class *is* defined, so you can obtain a reference to the
# [A] class (type) this way:
$classA = & $module { [A] }

$classA::new().printString()
  • 如您所见,这个需要修改源代码

  • 如果您使用带有 PowerShell 扩展的 Visual Studio 代码,您可以避免这种情况,如 所示。

这种不幸的行为是well-known issue, and 目前没有真正的好的解决方案

根本原因有点复杂(该行为在 5 年以上后仍然存在的部分原因),但它基本上是以下三方面的冲突:

  • PowerShell 中的模块生命周期管理(模块应该 可重新加载)
  • .NET 中的类型定义生命周期管理(一个类型在进程的生命周期内永远不会是“未定义的”*)
  • using module 提供自定义类型的解析时间解析的方式 - 简而言之,它并不是一个特别成熟 的功能

虽然没有好的解决方案,但 VSCode PowerShell 扩展有一个配置选项 allowing you to run debug sessions in a temporary shell,因此它不是问题:

{
  "powershell.debugging.createTemporaryIntegratedConsole": true
}

设置后,您可以对 testing/debugging 使用以下工作流程:

  1. 在编辑器中打开脚本
  2. 运行 通过调试器 (Shift+Ctrl+D -> Run and Debug)
  3. 观察 printString() 打印 111
  4. 修改模块文件,保存
  5. 运行 再次通过调试器运行脚本
  6. 观察 printString() 现在打印新值