我开发了一个PowerShell模块,它依赖于.NET程序集进行某些操作。 我重构了这个模块,不需要那个程序集,并注意到一些奇怪的行为,这阻止我最后删除该类:
Add-Type -TypeDefinition "public interface ICanTalk { string talk(); }" -Language CSharp
class Talker : ICanTalk {
[string] talk() { return "Well hello there"; }
}
如果以交互方式运行此命令,则会成功。但是只要我在ISE或psm1文件中运行它“en-bloque”,它就会抛出一个错误,说它无法找到Add-Type
调用中定义的接口。
我可以在Windows-PowerShell和PowerShell Core(6.0.2)中重现该问题
不同行为的原因是什么?我该如何解决?
PowerShell class
和enum
定义在执行开始之前进行解析,此类定义中引用的任何类型必须是:
using module
/ using assembly
语句加载。
using module
已经有效,但只有当引用的类型本身是class
/ enum
定义时。
目前,尚未检测到从程序集加载的类型 - 无论是通过包含使用using module
或using assembly
导入的程序集的模块来直接加载程序集。
请参阅以下GitHub问题:
#3461(using module
)和#2074(using assembly
)
#6652 - 跟踪所有与课程相关的问题的元问题。同时,解决方法是通过使用模块清单的NestedModules
条目,预先通过单独的脚本加载引用的类型。
ScriptsToProcess
条目也可以工作,但它引用的脚本是dot-sourced(加载到调用者的当前范围内),而不是封闭模块。
使用来自已编译代码的类型(包括带有Add-Type
的ad hoc编译代码),它也可以工作,因为这些类型总是可以在全局会话中使用,但是在概念上使用NestedModules
更简洁,因为它在封闭模块的范围内点源脚本显然,PowerShell将在执行代码之前读取文件并处理类定义。
要解决这个问题,需要将Add-Type
放入一个自己的脚本文件中,该文件在模块加载之前运行(或者在ISE中,只需在运行类定义之前运行代码)。
这可以通过使用PSD1文件中的ScriptsToProcess来完成。
感谢@TheIncorrigible1让我踏上了赛道。
为什么不在单独的文件中定义C#中的所有类,而不是将其添加为Add-Type -path MyClasses.cs这样它将适用于较旧的PS版本