【问题标题】:Powershell 5.# add type accelerator and use in class in same filePowershell 5.#添加类型加速器并在同一文件中的类中使用
【发布时间】:2020-07-11 08:10:10
【问题描述】:

我在 PS 5.1 中添加新型加速器时遇到了麻烦。我找到了this,以及其他一些类似的参考资料,所以我的理解是,PowerShell 的所有 5.# 版本都是这种情况,而不仅仅是参考的预览版。为此,我有这个

CLS
$accelerators = [PowerShell].Assembly.GetType("System.Management.Automation.TypeAccelerators")
$accelerators::Add("pxList",[System.Collections.Generic.List[string]])
$accelerators::Add("pxHashList",[System.Collections.Generic.List[hashtable]])
$builtinTypeAccelerators = $accelerators.GetField("builtinTypeAccelerators", [System.Reflection.BindingFlags]"Static,NonPublic")
$builtinTypeAccelerators.SetValue($builtinTypeAccelerators, $accelerators::Get)

class pxT_DefinitionsMigrater {
    # Properties
    $XML = [xml.xmlDocument]::new()
    $Errors = [pxList]::new()
    $initiations = [pxHashList]::new()

    # Constructor
    pxT_DefinitionsMigrater ([string]$xmlPath) {
    }
}

[pxT_DefinitionsMigrater]::new('String')

我仍然收到Unable to find type 错误。我哪里错了?有趣的是,这确实有效。

CLS
$accelerators = [PowerShell].Assembly.GetType("System.Management.Automation.TypeAccelerators")
$accelerators::Add("pxList",[System.Collections.Generic.List[string]])
$accelerators::Add("pxHashList",[System.Collections.Generic.List[hashtable]])
$builtinTypeAccelerators = $accelerators.GetField("builtinTypeAccelerators", [System.Reflection.BindingFlags]"Static,NonPublic")
$builtinTypeAccelerators.SetValue($builtinTypeAccelerators, $accelerators::Get)

([pxHashList]::new()).GetType().FullName

似乎自定义类型加速器不适用于类(以相同的方式)。所以也许更重要的是,添加我们自己的类型加速器是否仍然被认为是好的做法,或者自 PS5 以来的这种变化是否表明我们真的不应该这样做?或者至少不与课程结合使用?

另外,顺便说一句,但肯定不值得提出实际问题,我注意到new 方法没有大写。当我在 ISE 中获得我的新类型的智能选项时,EqualsReferenceEquals 都大写,但 new 不是。我见过的每个new 都是如此。似乎其中一定包含了一些有用的信息。

编辑:非常有趣的发现,就在我最初的帖子之后。 这也有效

CLS
$accelerators = [PowerShell].Assembly.GetType("System.Management.Automation.TypeAccelerators")
$accelerators::Add("pxList",[System.Collections.Generic.List[string]])
$accelerators::Add("pxHashList",[System.Collections.Generic.List[hashtable]])
$builtinTypeAccelerators = $accelerators.GetField("builtinTypeAccelerators", [System.Reflection.BindingFlags]"Static,NonPublic")
$builtinTypeAccelerators.SetValue($builtinTypeAccelerators, $accelerators::Get)

class pxT_DefinitionsMigrater {
    # Properties
    $XML = [xml.xmlDocument]::new()
    $Errors = $null
    $initiations = $null

    # Constructor
    pxT_DefinitionsMigrater ([string]$xmlPath) {
        $this.Errors = [pxList]::new()
        $this.initiations = [pxHashList]::new()
    }
}

[pxT_DefinitionsMigrater]::new('String')

因此,可以在属性部分为 XML 属性分配一个实际值,即空 [xml.document],但我无法在那里创建新类型的新空版本,我需要在 ctor 中进行。让我想知道是否有一个参数总是在声明时将属性设置为$null,然后在构造函数中赋值。所以...

CLS
$accelerators = [PowerShell].Assembly.GetType("System.Management.Automation.TypeAccelerators")
$accelerators::Add("pxList",[System.Collections.Generic.List[string]])
$accelerators::Add("pxHashList",[System.Collections.Generic.List[hashtable]])
$builtinTypeAccelerators = $accelerators.GetField("builtinTypeAccelerators", [System.Reflection.BindingFlags]"Static,NonPublic")
$builtinTypeAccelerators.SetValue($builtinTypeAccelerators, $accelerators::Get)

class pxT_DefinitionsMigrater {
    # Properties
    [xml.xmlDocument]$XML = $null
    [pxList]$Errors = $null
    [pxHashList]$initiations = $null

    # Constructor
    pxT_DefinitionsMigrater ([string]$xmlPath) {
        $this.XML = [xml.xmlDocument]::new()
        $this.Errors = [pxList]::new()
        $this.initiations = [pxHashList]::new()
    }
}

[pxT_DefinitionsMigrater]::new('String')

如果您有许多 ctor 变体,这似乎有点浪费,但也许是最佳实践?

编辑:确实,似乎是第二次运行才能让事情正常工作,而不是在使用加速器的地方。鉴于这最终将是一个脚本而不是在 ISE 中运行,因此在这两个地方都适用的答案将是理想的。

$accelerators = [PowerShell].Assembly.GetType("System.Management.Automation.TypeAccelerators")
$accelerators::Add("pxList",[System.Collections.Generic.List[string]])
$accelerators::Add("pxHashList",[System.Collections.Generic.List[hashtable]])
$builtinTypeAccelerators = $accelerators.GetField("builtinTypeAccelerators", [System.Reflection.BindingFlags]"Static,NonPublic")
$builtinTypeAccelerators.SetValue($builtinTypeAccelerators, $accelerators::Get)

class pxT_DefinitionsMigrater {
    # Properties
    [xml.xmlDocument]$XML
    [pxList]$Errors
    [pxHashList]$initiations

    # Constructor
    [Void]initializeProperties () {
        $this.XML = [xml.xmlDocument]::new()
        $this.Errors = [pxList]::new()
        $this.initiations = [pxHashList]::new()
    }
    pxT_DefinitionsMigrater ([string]$xmlPath) {
        $this.initializeProperties()
    }
}

[pxT_DefinitionsMigrater]::new('String')

【问题讨论】:

  • 嗯,另一件事。如果您分两次执行,看起来就像您的第一个示例工作。这意味着如果您选择自定义加速器然后 F8 然后加载剩余的东西(类定义 + 新对象)

标签: powershell type-accelerators


【解决方案1】:

包含类定义的 Powershell 代码首先评估它们。

# works!
[myClass]::new()
Start-Sleep -Seconds 3
class myClass{}          # this line runs first
# throws!
f               # this line runs first
function f {}

f: The term 'f' is not recognized as the name of a cmdlet, function, ...

问题是 pxT_DefinitionsMigrater 类正在尝试使用尚未定义的类型。

解决办法是

  1. 在单独的文件中定义类型和类。
  2. 先加载类型定义文件,然后加载类定义。

更新

可以基于this answer在单个文件中定义类型和类

function GetPxLists {
    if (("pxList" -as [type]) -eq $null){
        $accelerators = [PowerShell].Assembly.GetType("System.Management.Automation.TypeAccelerators")
        $accelerators::Add("pxList",[System.Collections.Generic.List[string]])
        $accelerators::Add("pxHashList",[System.Collections.Generic.List[hashtable]])
        $builtinTypeAccelerators = $accelerators.GetField("builtinTypeAccelerators", [System.Reflection.BindingFlags]"Static,NonPublic")
        $builtinTypeAccelerators.SetValue($builtinTypeAccelerators, $accelerators::Get)
    }

    [pxList]::new(), [pxHashList]::new()
}


# and inside class
    # Constructor
    pxT_DefinitionsMigrater ([string]$xmlPath) {
        $PxLists = GetPxLists
        $this.Errors = $PxLists[0]
        $this.initiations = $PxLists[1]
    }

【讨论】:

  • 有趣,因为它似乎不可能拥有一个带有类和类型加速器的自包含模块,这似乎是一种常见的事情。但是,这是微软,所以我想我必须选择课程或加速器,我会上课。加速器很方便,类解决了管道污染等问题,仅此一点就值得失去自定义加速器:-) 尽管如此,还是希望看到 MS 做对了。我们不应该选择任何一个或。
  • 找到了更好的答案stackoverflow.com/a/35220091/13388552。 @Gordon,您能否将问题设为“重复”(没有被某人标记)?
  • 这是一个有趣的切线。快速浏览显示该解决方案适用于方法,而我至少尝试在属性默认值中使用自定义类型加速器。但是,关于编译的一点确实帮助我理解了一点。这些类不只是加载,它们必须编译才能使用,然后从第一行开始执行。无论如何,现在看看我是否可以将其作为属性的默认值。如果可以,我会将其标记为重复。
  • IMO 您不必选择。模块可以很好地自包含。该模块不仅可以包含 psm1/psd1 文件。加速器和类可以位于模块中包含的 2 个附加数据文件中,并首先在 psm1 中加载。实际上,您可能可以使用ScriptsToProcess PSD1 元素顺序加载这些额外的文件,它们将自动包含在已发布的模块中。
  • @sage-pourpre 看起来我有一些研究要做。我熟悉的唯一模块是 PSM1 文件。从 PSM1 加载类已经有点奇怪了。但是,继续阅读清单。 :)
猜你喜欢
  • 2020-09-19
  • 2016-06-23
  • 1970-01-01
  • 2020-11-09
  • 1970-01-01
  • 1970-01-01
  • 2019-10-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多