我有一个模块设置就像一个其他脚本的库。我无法弄清楚如何在调用Import-Module
的脚本范围中获取类声明。我试图安排Export-Module
与-class
论点,如-function
,但没有-class
可用。我只需要在每个脚本中声明该类吗?
设置:
import-module holidays
这是这个类的样子:
Class data_block
{
$array
$rows
$cols
data_block($a,$r,$c)
{
$this.array = $a
$this.rows = $r
$this.cols = $c
}
}
我已经尝试了这个页面上的每一种方法,我已经找到了一个可以重复使用的方法。我发现模块缓存是最令人沮丧的方面...我可以将类作为模块导入,甚至可以使代码工作,但是powershell只能间歇性地识别新类...它几乎像Powershell一样忘记你曾经添加过一个.. 。
我找到了一种加载类的方法,而无需“使用模块”。在MyModule.psd1文件中使用以下行:
ScriptsToProcess = @('Class.ps1')
然后将您的类放在Class.ps1文件中:
class MyClass {}
更新:虽然您不必在此方法中使用“使用模块MyModule”,但您仍需要:
PSA:有一个已知的问题是将旧的类副本保存在内存中。如果您不了解它,那么使类的处理真的很混乱。你可以读一下here。
using
容易陷入陷阱using
关键字容易出现以下各种陷阱:
using
语句中指定模块的完整路径,否则PSModulePath
语句不适用于不在using
中的模块。这是相当令人惊讶的,因为虽然可以通过Get-Module
获得一个模块,但using
语句可能不起作用,具体取决于模块的加载方式。using
语句只能在“脚本”的最开头使用。没有[scriptblock]::Create()
或New-Module
的组合似乎克服了这一点。传递给Invoke-Expression
的字符串似乎充当了一种独立的脚本;在这种字符串类型的作品开头的using
声明。也就是说,Invoke-Expression "using module $path"
可以成功,但模块内容可用的范围似乎是不可理解的。例如,如果在Pester脚本块中使用Invoke-Expression "using module $path"
,则模块内的类不能从相同的Pester脚本块中获得。以上陈述基于this set of tests。
ScriptsToProcess
阻止访问专用模块功能在模块清单的ScriptsToProcess
引用的脚本中定义类似乎乍一看从模块中导出类。但是,它不是出口类,而是"creates the class in the global SessionState instead of the module's, so it...can't access private functions"。据我所知,使用ScriptsToProcess
就像以下方式在模块外定义类:
# this is like defining c in class.ps1 and referring to it in ScriptsToProcess
class c {
[string] priv () { return priv }
[string] pub () { return pub }
}
# this is like defining priv and pub in module.psm1 and referring to it in RootModule
New-Module {
function priv { 'private function' }
function pub { 'public function' }
Export-ModuleMember 'pub'
} | Import-Module
[c]::new().pub() # succeeds
[c]::new().priv() # fails
调用此结果
public function
priv : The term 'priv' is not recognized ...
+ [string] priv () { return priv } ...
即使从导入该模块时定义的类调用priv
,也无法从类中访问模块函数priv
。这可能是你想要的,但我没有找到它的用途,因为我发现类方法通常需要访问我想要保密的模块中的某些函数。
.NewBoundScriptBlock()
似乎可靠地工作调用绑定到包含该类的模块的scriptblock似乎可靠地工作以导出类的实例,并且不会遭受using
所做的陷阱。考虑这个包含类并已导入的模块:
New-Module 'ModuleName' { class c {$p = 'some value'} } |
Import-Module
在绑定到模块的scriptblock中调用[c]::new()
会生成[c]
类型的对象:
PS C:\> $c = & (Get-Module 'ModuleName').NewBoundScriptBlock({[c]::new()})
PS C:\> $c.p
some value
.NewBoundScriptBlock()
的惯用语替代品似乎有一个较短的,惯用的替代.NewBoundScriptBlock()
。以下两行分别在Get-Module
输出的模块的会话状态中调用scriptblock:
& (Get-Module 'ModuleName').NewBoundScriptBlock({[c]::new()})
& (Get-Module 'ModuleName') {[c]::new()}}
后者的优点是,当对象被写入管道时,它将产生对管道中间脚本块的控制流。另一方面,.NewBoundScriptBlock()
收集写入管道的所有对象,并且只有在完成整个脚本块的执行后才会产生。
这当然不能按预期工作。 PS 5中的想法是,您可以在具有.psm1扩展名的单独文件中定义您的类。 然后您可以使用命令加载定义(例如):
using module C:\classes\whatever\path\to\file.psm1
这必须是脚本中的第一行(注释后)。 造成这么大痛苦的原因是,即使从脚本调用类定义,也会为整个会话加载模块。你可以通过运行来看到这个:
get-module
您将看到加载的文件的名称。无论你是否再次运行脚本,它都不会重新加载类定义! (甚至不会读取psm1文件。)这会导致牙齿咬牙切齿。 有时 - 有时 - 您可以在运行脚本之前运行此命令,该脚本将使用刷新的类定义重新加载模块:
remove-module file
其中file是没有路径或扩展名的名称。但是为了节省您的理智,我建议您重新启动PS会话。这显然很麻烦;微软需要以某种方式清理它。
你几乎不能。根据about_Classes
的帮助:
类关键字
定义一个新类。这是一个真正的.NET Framework类型。类成员是公共的,但只在模块范围内公开。您不能将类型名称称为字符串(例如,New-Object不起作用),并且在此版本中,您不能在脚本之外使用类型文字(例如,[MyClass])定义类的模块文件。
这意味着,如果你想让自己获得一个data_block
实例或使用操作这些类的函数,那么创建一个函数,比如New-DataBlock
并让它返回一个新的data_block
实例,然后你可以使用它来获取类方法和属性(可能包括静态的)。
我在v5中遇到了有关PowerShell类的多个问题。
我现在决定使用以下解决方法,因为它与.net和PowerShell完全兼容:
Add-Type -Language CSharp -TypeDefinition @"
namespace My.Custom.Namespace {
public class Example
{
public string Name { get; set; }
public System.Management.Automation.PSCredential Credential { get; set; }
// ...
}
}
"@
好处是您不需要自定义程序集来添加类型定义,您可以在PowerShell脚本或模块中内联添加类定义。
唯一的缺点是,您需要创建一个新的运行时,以便在第一次加载之后重新加载类定义(就像在c#/ .net域中加载程序集一样)。
我解决这个问题的方法是将自定义类定义移动到一个具有相同名称的空.ps1文件中(就像在Java / C#中一样),然后将其加载到模块定义和依赖代码中点源。我知道这不是很好,但对我来说,最好不要在多个文件中维护同一个类的多个定义......
要在开发时更新类定义,请选择类的代码,然后按F8
运行所选代码。不像-Force
命令中的Import-Module
选项那样干净。看作使用Module
没有那个选项,Remove-Module
充其量只是零星的,这是我发现开发课程并查看结果而不必关闭ISE并重新启动它的最佳方法。
using声明是它适合你的方式。否则这似乎也有效。
testclass.psm1
使用函数来提供课程
class abc{
$testprop = 'It Worked!'
[int]testMethod($num){return $num * 5}
}
function abc(){
return [abc]::new()
}
Export-ModuleMember -Function abc
someScript.ps1
Import-Module path\to\testclass.psm1
$testclass = abc
$testclass.testProp # returns 'It Worked!'
$testclass.testMethod(500) # returns 2500
$testclass | gm
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
testMethod Method int testMethod(System.Object num)
ToString Method string ToString()
testprop Property System.Object testprop {get;set;}