首页 > 解决方案 > PowerShell IComparable 与子类

问题描述

假设我们有这 3 个类:

Class BaseClass : System.IComparable
{
    [int] $Value
    BaseClass([int] $v)
    {
        $this.Value = $v
    }
    [int] CompareTo($that)
    {
        If (-Not($that -is [BaseClass])) {
            Throw "Not comparable!!"
        }
        return $this.Value - $that.Value
    }
}
Class SubClassA : BaseClass
{
    SubClassA([int] $v) : base($v)
    {
    }
}
Class SubClassB : BaseClass
{
    SubClassB([int] $v) : base($v)
    {
    }
}

当我们比较以下实例时,此实现效果很好BaseClass

$base1 = [BaseClass]::new(1)
$base2 = [BaseClass]::new(2)
Write-Output ($base1 -lt $base2)
# Output: True
Write-Output ($base1 -gt $base2)
# Output: False

但是我找不到比较两个子类的两个实例的方法:

$subA1 = [SubClassA]::new(1)
$subB2 = [SubClassB]::new(2)
Write-Output (([BaseClass]$subA1) -lt ([BaseClass]$subB2))

PowerShell 无法执行此代码,引发此错误:

Impossibile confrontare "SubClassA" con "SubClassB".
Errore:
    "Impossibile convertire il valore "SubClassB" di tipo "SubClassB" nel tipo "SubClassA"."

从意大利语翻译成英语,这个错误信息听起来像:

Unable to compare "SubClassA" with "SubClassB".
Error:
    "Unable to convert the value "SubClassB" of type "SubClassB" to the type "SubClassA"."

为什么会出现这个错误?我们如何将 的实例SubClassA与 的实例进行比较,SubClassB就好像它们是 的两个实例一样BaseClass

PS:输出$PSVersionTable

PSVersion                      5.1.17134.1
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.17134.1
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

标签: powershellicomparable

解决方案


这种行为确实令人惊讶,我看到您已经打开了一个 GitHub 问题来讨论它。

一些额外的想法:

  • PowerShell 的运算符通常具有扩展的语义,通常不应假定在所有情况下都与 C# 对应的运算符相同 - 但是,在这种特殊情况下,它们显然应该(见下文)。

  • -lt实际上与基类实例一起工作的事实已经指出了一个区别:等效的 C# 类还需要显式重载<and>运算符,以支持使用<.

  • 接口实现由 PowerShell 和 C# 中的派生类继承,因此无需强制转换为基类即可应用-lt.


这里似乎发生的是,PowerShell 盲目地尝试将 RHS 转换为 LHS 的类型,而不考虑它们的共享基类。

如果 RHS 类型直接派生自 LHS 类型,则不会出现问题(反之亦然);例如:

$base1 -lt $subA1  # OK: SubClassA (RHS) derives from BaseClass (LHS) 

$subA1 -lt $base1  # !! BREAKS: BaseClass (RHS) does NOT derive from SubClassA (LHS)

推荐阅读