PowerShell 类:是否可以将构造函数参数设置为可选?

PowerShell Classes: Is it possible to make a constructors parameter optional?

提问人:Ralf_Reddings 提问时间:9/6/2023 最后编辑:mklement0Ralf_Reddings 更新时间:9/6/2023 访问量:194

问:

我正在尝试为电影/动画中使用的时间码约定构建一个“类型”。

我想适应用户可能希望使用从其他地方获取的时间码字符串初始化对象的用例:

$MyTimeCode = [TimeCode]::new("08:06:04.10", 12)

在其他情况下,直接使用属于类构造函数的参数:

$MyTimeCode = [TimeCode]::new(, 8, 6, 4, 10, 12)

#If I had constructor such as the foloowing in my class definition:
#TimeCode([String]$TimeStamp, [Int]$Hours, [Int]$Minutes, [Int]$Seconds, [Int]$Frames, [Int]$FrameRate)
`

以下类为例:

class HelloWorld {
    [String]$Hello
    [String]$World

    HelloWorld([String]$Hello, [String]$World) {
        $This.Hello = $Hello
        $This.World = $World
        }
    }

尝试初始化它工作正常,但我收到错误:[HelloWorld]::new("Hello", "World")[HelloWorld]::new("Hello")

MethodException: Cannot find an overload for "new" and the argument count: "1".

我一直在看一些讲座,但它们根本没有结构,而且进展很快。

官方文档通常是我的首选,但“About_Classes”页面通过“机架”和“设备”示例迅速发展到“继承”和“子类”等,我不清楚如何开始只使用一个类。

我的目标是熟悉课程以及它们可以做什么。时间码是次要的。

PowerShell 构造函数 optional-parameters

评论


答:

7赞 lit 9/6/2023 #1

需要一个接受单个参数的构造函数。只要每个参数的签名是唯一的,就可以根据需要有任意数量的构造函数。此代码还会创建默认构造函数。默认构造函数的成员在实例化时将包含。$null

class HelloWorld {
    [String]$Hello
    [String]$World

    HelloWorld([String]$Hello, [String]$World) {
        $This.Hello = $Hello
        $This.World = $World
    }
    HelloWorld([String]$Hello) {
        $This.Hello = $Hello
        $This.World = 'default'
    }
    HelloWorld() {}     # default constructor
}

$x1 = [HelloWorld]::new('hello', 'world')
$x1
$x2 = [HelloWorld]::new('hello')
$x2
$x3 = [HelloWorld]::new()
$x3

评论

0赞 Doug Maurer 9/6/2023
很好。定义自定义构造函数时,还应包括默认的无参数构造函数。没有它,您将不会在智能意义上看到 New()。imgur.com/vxvYheMimgur.com/UUBvGhC
0赞 Doug Maurer 9/6/2023
好的,IntelliSense 是提供默认构造函数的一个糟糕论据。一个更好的论点是,当使用自定义构造函数时,默认构造函数会消失。这意味着您不能在没有参数的情况下实例化实例。这可能会让用户感到困惑,所以如果你愿意记下这一点。4sysops.com/archives/powershell-classes-part-4-constructors 提到了这个可能令人困惑的事实。
0赞 Doug Maurer 9/6/2023
我认为这比省略要好。我很感激你。
5赞 mklement0 9/6/2023 #2

要向 lit 的有效解决方案添加一些背景信息,请执行以下操作:

你要查找的功能需要以下功能之一,这些功能在 PowerShell 中不可用,在 Windows PowerShell(不再处于活动开发状态)中不可用,在积极开发的 PowerShell (Core)(截至 v7.3.x,截至撰写本文时的稳定版本)中不可用:

  • 可选的方法/构造函数参数,最好与命名参数结合使用。

    • 不支持可选参数,即使 PowerShell 允许定义它们

       # !! Does NOT work as of PowerShell 7.3.x
       # !! The default value is QUIETLY ACCEPTED, but IGNORED. 
       class Foo { [void] Bar($baz, $quux = 42) {} }
      
       # !! FAILS, because you must supply arguments for BOTH parameters.
       # !! -> 'Cannot find an overload for "Bar" and the argument count: "1"'
       [Foo]::new().Bar('hi')
      
    • 从根本上不支持命名参数。

  • 构造函数链接,即具有多个构造函数重载,这些重载可以使用默认值相互调用,PowerShell 从 v7.3.x 开始不支持:

       class Foo { 
         # Two-parameter constructor
         Foo($bar, $baz) { <# ... #> } 
         # Single-parameter constructor
         Foo($bar) { 
           # !! Constructor chaining Does NOT work as of PowerShell 7.3.x:
           # !! That is, the attempt to call the two-parameter constructor
           # !! (with a default value for the second parameter) causes a SYNTAX ERROR.
           $this($bar, 'default for $baz') 
         } 
       }
    
    • 有关通过隐藏帮助程序方法的解决方法,请参阅此答案

      • 在简单情况下,如果代码重复不是问题,您可以 只需创建单独的、独立的构造函数重载,如 LIT 的回答所示。
    • GitHub 问题 #19969 是一个功能请求,要求实现构造函数链接(以及其他功能)。

2赞 Santiago Squarzon 9/6/2023 #3

这是 C# 类允许您执行的操作,即:

public class HelloWorld
{
    public string? Hello { get; set; }
    public string? World { get; set; }

    public HelloWorld(string? hello = null, string? world = null)
    {
        Hello = hello;
        World = world;
    }
}

将允许在单个构造函数中实例化的 3 种可能性(无参数、1 个参数或 2 个参数)。PowerShell 类远非如此......相当令人失望。从好的方面来说,Add-Type 可用于内联 C#。

添加到其他答案的其他替代方案:

class HelloWorld {
    [string] $Hello
    [string] $World
}

[HelloWorld]@{ Hello = 'foo' }
[HelloWorld]@{ World = 'bar' }
class HelloWorld {
    [string] $Hello
    [string] $World

    HelloWorld([string] $Hello, [string] $World) {
        $this.Hello = $Hello
        $this.World = $World
    }

    # targets a `$hello` only explicit casting
    static hidden [HelloWorld] op_Explicit([string] $Hello) {
        return [HelloWorld]::new($Hello, 'myDefaultValue')
    }
}

[HelloWorld] 'foo'

按照开头的 C# 示例,Mathias 的有用回答中也显示了这一点。

注意:编译需要 PowerShell 7+,这将在 Windows PowerShell 5.1 中失败。

Add-Type '
public class HelloWorld
{
    public string? Hello { get; set; }
    public string? World { get; set; }

    public HelloWorld() : this("hey", "there")
    { }

    public HelloWorld(string? hello = "hey", string? world = "there")
    {
        Hello = hello;
        World = world;
    }
}' -IgnoreWarnings -WarningAction Ignore

[HelloWorld]::new()

# Hello World
# ----- -----
# hey   there

[HelloWorld]::new('hi')

# Hello World
# ----- -----
# hi    there

[HelloWorld]@{ world = 'you' }

# Hello World
# ----- -----
# hey   you