PowerShell和StringBuilder

38

我是PowerShell的新手,但熟悉.NET类。

我在PowerShell脚本中使用了System.Text.StringBuilder。脚本如下:

Function MyStringFunc([String]$line) {
    $r = New-Object -TypeName "System.Collections.Generic.List``1[[System.String]]";
    $sb = New-Object -TypeName "System.Text.StringBuilder";

    foreach ($c in $line) {
        $sb.Append($c);
        $r.Add($sb.ToString());
    }

    return $r;
}

$line1 = "123";
$a = MyStringFunc $line1;
$a

我期望的结果是

1
12
123

但是结果是

                  Capacity                MaxCapacity                    Length
                  --------                -----------                    ------
                        16                 2147483647                         3
123

我做错了什么吗?

3个回答

52

StringBuilder中的一些方法(如Append)返回StringBuilder本身,因此您可以调用更多的StringBuilder方法。但是PowerShell的工作方式是输出所有结果(在.NET方法调用的情况下返回值)。 在这种情况下,将结果转换为[void]以忽略返回值,例如:

[void]$sb.Append($c)
请注意,在PowerShell中,您不需要在行末使用;。但是,如果您将多个命令放在同一行上,则使用;来分隔这些命令。

谢谢,它可以用。但是这种行为在MSDN中没有注明,我能知道官方参考资料在哪里吗? - Alex Yeung
16
MSDN正确指出StringBuilder.Append(...)返回StringBuilder。在像C#这样的语言中,这并不是一个问题,因为你可以忽略返回值。然而,在PowerShell中,它会隐式地输出任何返回的值。如果你不想在PowerShell中输出返回的值,必须将表达式转换为[void](或将表达式管道传递到Out-Null)。这只是C#和基于shell的脚本语言PowerShell之间的一个基本差异。 - Keith Hill

7

因为你说你是PowerShell新手,所以我的回答更多地涉及到你写PowerShell的方式。

PowerShell是一种脚本语言,它被非开发人员程序员(如管理员)使用。你是一个拥有丰富知识的C#程序员,但是在PowerShell中,你可以更简单地编写想要的代码。在PowerShell中,有使用列表和哈希表的语法,尝试以下内容:

$a = @()
$a += "Bonjour"
$a += "Salut"
$a
$a | get-member
Get-Member -InputObject $a

$rwc = @{}
$rwc += @{1="Blues"}
$rwc += @{2="Blacks"}
$rwc
$rwc | get-member
Get-Member -InputObject $rwc

这里有三个函数,它们都完成同样的任务。我知道使用StringBuilder在内存方面更加高效,但在这种情况下谁会关心呢。

Function MyStringFunc([String]$line)
{ 
  $r = New-Object -TypeName "System.Collections.Generic.List``1[[System.String]]"; 
  $sb = New-Object -TypeName "System.Text.StringBuilder"; 

  foreach ($c in [Char[]]$line)
  { 
    $a = $sb.Append($c); 
    $r.Add($sb.ToString()); 
  }  
  return $r; 
} 

# A more simple way
Function MoreReadableStringFunction([String]$line)
{
  $r = @() # an untyped list

  foreach ($c in [Char[]]$line)
  { 
    $a += $c; 
    $r += $a;
  } 
  return $r;
}

# More simple but not so readable
Function MoreSimpleStringFunction([String]$line)
{
  $r = @() # an untyped list

  [Char[]]$line | % {$a += $_; $r += $a} 
  return $r;
}


Clear-Host
$line1 = "123";

$t1 = MyStringFunc $line1; 
$t1

$t2 = MoreReadableStringFunction $line1
$t2

$t3 = MoreSimpleStringFunction $line1
$t3

我不会说在PowerShell中使用+=和StringBuilder连接之间的差异无关紧要...它的效率指数级下降,在StringBuilder中执行相同操作可能只需要0.2秒,而在+=中则需要60秒。即使是适度数量的+=,这也是非常巨大的差异。使用+=的10000行循环的measure-command:TotalSeconds:49.0461414秒--使用stringbuilder的相同循环:TotalSeconds:0.2216707秒,由measure-command测量。 - danekan

3
  1. foreach不会遍历字符串中的字符,而是将其视为单个项。因此,我们必须将字符串强制转换为[char[]](或使用$line.GetEnumerator())。

  2. 表达式$sb.Append($c)获取了构建器实例,在PowerShell中它会被写入输出。如果不需要,则应该抑制此输出($null = ...... > $null... | Out-Null)。

这段代码产生了预期的输出:

Function MyStringFunc([String]$line) {
    $r = New-Object -TypeName "System.Collections.Generic.List``1[[System.String]]";
    $sb = New-Object -TypeName "System.Text.StringBuilder";

    foreach ($c in [char[]]$line) {
        $null = $sb.Append($c);
        $r.Add($sb.ToString());
    }

    return $r;
}

$line1 = "123";
$a = MyStringFunc $line1;
$a

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接