你有没有注意到有些人使用 StringBuilder. in their PowerShell scripts and wondered why? The whole point of using StringBuilder. is to concatenate or build large strings. There are ways to do this in PowerShell already, so why would someone turn to this DotNet object?

他们正试图优化性能,因为在PowerShell中加入字符串的简单方法可以很快变得昂贵。让我们来看看这样的方式和其他方式我们可以连接字符串。

指数

设置我们的范围

本文的重点将在加入大量的值或文本行。想想在循环中添加到循环中的字符串的方案。我不会专注于您只是在单行上格式化值的较小字符串。

有了那么说,我确实有另一篇帖子我覆盖 您想要了解字符串中变量替换的一切.

StringBuilder.解决了什么问题?

我们应该首先来看看这个问题。 PowerShell和Dotnet中的字符串是不可变的。来自 mdsn字符串类 documentation:

字符串对象称为不可变(只读),因为它在创建后无法修改其值。似乎修改String对象的方法实际上返回包含修改的新字符串对象。

因为字符串是不可变的,所以对似乎单个字符串的重复添加或删除执行重复添加或删除的字符串操作例程可以确切地进行显着的性能损失。

在该下一个示例中,在内存中创建至少5个字符串。

$example = 'First'
$example += 'Second'
$example += 'Third'

We have the obvious literals First,Second,Third. $example starts with a value of First. Then a new string is created with the value FirstSecond and assigned to $example. Then a new string is created with the value FirstSecondThird and assigned to $example. All of that sits in ram until garbage collection.

Not only are we using a lot of RAM that is not needed, we create a lot of data copy operations. To create the string of FirstSecond, we have to allocate room for each character. We copy in the 5 characters First one at a time and then we repeat the process with the Second string. When we get to FirstSecondThird, the process starts over copying all the characters again. It looks like a simple operation to us, but PowerShell is doing a lot of work that we don’t see.

使用StringBuilder.

StringBuilder. 是构建字符串的DotNet对象。您可以在此示例中看到基础知识。

$sb = [System.Text.StringBuilder]::new()
[void]$sb.Append( 'Was it a car ' )
[void]$sb.AppendLine( 'or a cat I saw?' )
$sb.ToString()

I start by creating a StringBuilder. and then appending a few strings to it. I am using the [void] to suppress the output from the append functions. To get the string from the StringBuilder., we need to call ToString().

StringBuilder. 使用内部数据结构,该数据结构用于快速添加数据。此对象的整体目的是解决我之前概述的性能问题。

StringBuilder.由数字

I pulled together a simple test to show how much faster StringBuilder. can be.

$string = ''
Measure-Command {
    foreach( $i in 1..10000)
    {
        $string += 'Was it a car or a cat I saw? '
    }
    $string
}
#TotalMilliseconds : 1588.2549

$sb = [System.Text.StringBuilder]::new()
Measure-Command {
    foreach( $i in 1..10000)
    {
        [void]$sb.Append( 'Was it a car or a cat I saw? ')
    }
    $sb.ToString()
}
#TotalMilliseconds : 127.509

超过10万迭代显示它们之间的巨大差距。

转回Powershell

The use of StringBuilder. comes from the C# or DotNet world. If we change our requirements a bit then there is another option available to us. Sometimes we need a single multi-line string and other times all we need is a collection of strings. If a collection of strings is what we are looking for, we can leverage the PowerShell pipeline.

I have 2 pipeline examples. The first is collecting the output of a foreach loop and the second is a function with a process block.

管道由数字

让我们再次使用这些方案进行测试。

$sb = [System.Text.StringBuilder]::new()
Measure-Command {
    foreach( $i in 1..1000000)
    {
        [void]$sb.AppendLine( 'Was it a car or a cat I saw?')
    }
    $sb.ToString()
}
#TotalMilliseconds : 1234.2676


$foreach = @()
Measure-Command {
    $foreach = foreach( $i in 1..1000000)
    {
        'Was it a car or a cat I saw?'
    }
    $foreach
}
#TotalMilliseconds : 846.7726


function PipelineTest{
    process{
        'Was it a car or a cat I saw?'
    }
}
$pipeline = ''
Measure-Command {
    $pipeline = 1..1000000 | PipelineTest
    $pipeline
}
#TotalMilliseconds : 1164.6559

这些数字彼此更近。我真的只是想表明我们有一些选择,具体取决于我们的需求。

把它包裹起来

There was a time where I would turn to StringBuilder. quite quickly. As I got a better understanding of the PowerShell pipeline, I found myself using it more for situations like this. It’s good to know the options and when you can shift your requirements for better results.

这里有一个常见的谚语,当谈到PowerShell的表现时,我会给你带来的。

如果性能很重要,测试它。