【问题标题】:How to properly call static methods of types added with Add-Type from within Powershell classes?如何从 Powershell 类中正确调用使用 Add-Type 添加的类型的静态方法?
【发布时间】:2017-02-11 20:39:25
【问题描述】:

我正在添加这个type 并调用它。它在脚本/函数中运行良好。一旦我试图从 powershell 类中调用它 - 它会给出错误“类型未定义”。

我已经设法使用一些丑陋的黑客来称呼它。 Add-Type-PassThru 调用,结果保存在$global:myType 中。然后我使用$global:myType.GetMethod('GetSymbolicLinkTarget').invoke($null,$dir)进行间接调用。

有没有更好的解决方案?

PS:每次运行前都会重置运行空间(ISE 中为 Ctrl-T,PowerGUI 和 Powershell Studio 中为自动)。

PS2:工作示例(简化)如下。

#works - returns 2
$Source = ' public class BasicTest { public static int Test(int a) { return (a + 1); } } '
Add-Type -TypeDefinition $Source
[BasicTest]::Test(1)


#gives error: Unable to find type [BasicTest]
$Source = ' public class BasicTest { public static int Test(int a) { return (a + 1); } } '
Add-Type -TypeDefinition $Source
class foo { Static [int]bar() { return [BasicTest]::Test(1) } }
[foo]::bar()


#workaround 1 - indirect call with GetMethod(...).Invoke(...)
# returns 4
$Source = ' public class BasicTest1 { public static int Test(int a) { return (a + 1); } } '
$global:BasicTestType1 = (Add-Type -TypeDefinition $Source -PassThru)
class foo {
    static $BasicTestType2 = (Add-Type -TypeDefinition ' public class BasicTest2 { public static int Test(int a) { return (a + 1); } } ' -PassThru)
    Static [int]bar() {
        $ret = $global:BasicTestType1.GetMethod('Test').Invoke($null, [int]1)
        $ret += [foo]::BasicTestType2.GetMethod('Test').Invoke($null, [int]1)
        return $ret
    }
}
[foo]::bar()


#workaround 2 - invoke-expression; has problems passing parameters
# returns 2
$Source = ' public class BasicTest { public static int Test(int a) { return (a + 1); } } '
Add-Type -TypeDefinition $Source
class foo { Static [int]bar() { return invoke-expression '[BasicTest]::Test(1)' } }
[foo]::bar()

PS3:PetSerAl 提供了另外两个解决方法here

【问题讨论】:

  • 您是否已经尝试过他们在链接代码 sn-p 末尾所做的方式? [System.Win32]::GetSymbolicLinkTarget($dir)这是公认的答案,OP说它有效......
  • Add-Type <...>; class foo { Static [void]bar() { [System.Win32]::GetSymbolicLinkTarget('c:\') } } - 它甚至无法编译。 PS:每次运行前都会重置运行空间。
  • 在静态方法中移动 Add-Type 也无济于事。
  • Invoke-Expression 'here go code using the added type' 可能吗?
  • 您能否提供示例类型以及您如何重置运行空间以及何时重置?

标签: class powershell


【解决方案1】:

问题是类在编译时被解析,并且对你添加的类型的引用直到运行时才被添加。因此,无法解析引用。

我看到的最佳解决方案是强制它们的编译顺序。

# Add the type
$Source = ' public class BasicTest { public static int Test(int a) { return (a + 1); } } '
Add-Type -TypeDefinition $Source

$scriptBlockText = @"
  class foo {
    Static foo() {
    }
   Static [int]bar() { return [BasicTest]::Test(1) } 
  }
"@

# Create a script block from the text
# the class will be compiled at this point
# but is not in scope yet
$scriptBlock = [scriptblock]::Create($scriptBlockText)

# dot source the class
# this brings the class into scope
.([scriptblock]::Create($scriptBlock))

# it would be better if the class was in a separate file
# but I'm using a string to simplify the sample.
# for a file the syntax would be `. .\myClass.ps1`
# this would replace both creating the script block and 
# the dot sourcing statement

# Now you can use the class and the type you addded earlier
[foo]::bar()

【讨论】:

  • 在我的例子中,真正的课程大约是 300 行,所以放入文本中很痛苦。在这一点上,我对 ps 多文件项目已经吃饱了。似乎我的 C# 思维方式还没有适应 Powershell。所以我的多文件 ps 项目非常不稳定 - 一次更改,它们突然停止正常工作。
  • 正如答案所说,您应该将 PowerShell 类放在单独的 ps1 文件中。 C# 类也可以从.CS 文件中读取。
【解决方案2】:

另外两种解决方法:

#workaround 3 - type variable
# returns 4
$Source = ' public class BasicTest1 { public static int Test(int a) { return (a + 1); } } '
$global:BasicTestType1 = (Add-Type -TypeDefinition $Source -PassThru)
class foo {
    static $BasicTestType2 = (Add-Type -TypeDefinition ' public class BasicTest2 { public static int Test(int a) { return (a + 1); } } ' -PassThru)
    Static [int]bar() {
        $ret = ($global:BasicTestType1)::Test(1)
        $ret += ([foo]::BasicTestType2)::Test(1)
        return $ret
    }
}
[foo]::bar()


#workaround 4 - most elegant so far
# returns 2
class foo {
    static foo() {
        $Source = ' namespace foo { public class BasicTest1 { public static int Test(int a) { return (a + 1); } } } '
        Add-Type -TypeDefinition $Source 
    }
    Static [int]bar() {
        $ret = ([type]'foo.BasicTest1')::Test(1)
        return $ret
    }
}
[foo]::bar()

解决方法 4 是最短且最干净的。

也可以使用类型加速器。

【讨论】:

    猜你喜欢
    • 2023-03-27
    • 1970-01-01
    • 2017-01-05
    • 2015-03-09
    • 2023-04-05
    • 2016-11-23
    • 2021-03-08
    • 2010-10-10
    相关资源
    最近更新 更多