The .NET toolbox
Reach past cmdlets into .NET: static methods, fast collections, regular expressions and timing your code.
- Call static .NET methods and create .NET objects with ::new()
- Choose between arrays, List, HashSet, Queue and Stack - and know why += on arrays is slow
- Find every match with [regex]::Matches, transform text with -replace and a script block, and measure speed with Measure-Command
Every PowerShell object is a .NET object - and the whole .NET library is open to you. Two bits of syntax unlock it:
[Type]::Membercalls a static method or property that belongs to the type itself:[math]::Sqrt(81),[guid]::NewGuid(),[System.IO.Path]::GetExtension('map.png').[Type]::new(...)creates an instance, just as you did with your own classes.
Unsure what a type offers? Pipe it to Get-Member -Static, or type [math]:: and press Tab.
1[math]::Sqrt(81)
2[math]::Max(12, 9)
3[math]::Round(7.4567, 2)
4[System.IO.Path]::GetFileNameWithoutExtension('ember-portrait.png')
5[System.IO.Path]::ChangeExtension('notes.txt', '.md')
6[string]::IsNullOrWhiteSpace(' ')
7[string]::Join(' & ', 'Ada', 'Grace', 'Linus')
8[char]::IsUpper('Ember'[0])
9[int]::MaxValue9 12 7.46 ember-portrait notes.md True Ada & Grace & Linus True 2147483647
Collections with superpowers
PowerShell arrays are fixed size. $list += $item doesn’t append - it builds a brand-new array, copies every element across, and adds one. Fine for 50 items; painfully slow for 50,000. The System.Collections.Generic namespace has better tools:
List[T]- a growable list..Add()is cheap,.Remove()and.Contains()are built in.HashSet[T]- unique values only, with instant lookups..Add()returns$falsefor duplicates. It’s case-sensitive unless you pass[StringComparer]::OrdinalIgnoreCase.Queue[T]- first in, first out:.Enqueue()and.Dequeue(). A feeding line.Stack[T]- last in, first out:.Push()and.Pop(). An undo history.Dictionary[K,V]- a typed hashtable.
Adding to a List or HashSet returns a value (an index or a bool), so pipe it to $null or use [void] to keep it out of your output.
1$names = [System.Collections.Generic.List[string]]::new()
2$names.Add('Ember')
3$names.Add('Glim')
4$names.Insert(0, 'Skyla')
5$names -join ', '
6$seen = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase)
7$seen.Add('Ember')
8$seen.Add('EMBER')
9$seen.Count
10$line = [System.Collections.Generic.Queue[string]]::new()
11'Ember', 'Glim', 'Pebble' | ForEach-Object { $line.Enqueue($_) }
12"first fed: $($line.Dequeue()), next up: $($line.Peek())"
13$undo = [System.Collections.Generic.Stack[string]]::new()
14'paint wall', 'move rock' | ForEach-Object { $undo.Push($_) }
15"undo: $($undo.Pop())"Skyla, Ember, Glim True False 1 first fed: Ember, next up: Glim undo: move rock
How much faster? Measure-Command { ... } runs a block and returns a TimeSpan (output inside the block is discarded). Try this on your own machine - exact numbers vary, but the gap is dramatic in Windows PowerShell 5.1.
1$arrayTime = Measure-Command {
2 $array = @()
3 foreach ($i in 1..10000) { $array += $i }
4}
5$listTime = Measure-Command {
6 $list = [System.Collections.Generic.List[int]]::new()
7 foreach ($i in 1..10000) { $list.Add($i) }
8}
9"array += : $([int]$arrayTime.TotalMilliseconds) ms"
10"List.Add(): $([int]$listTime.TotalMilliseconds) ms"Try it
Pick the collection
Choose the best tool for each job.
“Collect 100,000 log lines one at a time in a loop”
“Remember which dragons have already been fed today”
“Serve dragons in the order they arrived at the trough”
“Undo the last thing a keeper changed on the map”
“Find the unique species among 5,000 sightings”
“Explore cave tunnels, backtracking to the most recent fork”
“Build a list of names, then insert a VIP at the front”
“Process vet appointments in booking order”
Regular expressions, all of them
-match is great for a yes/no answer, but it only fills $Matches with the first match. To harvest every match, use [regex]::Matches(text, pattern) - each result has .Value and .Groups. Name your groups with (?<name>...) to make code readable.
And -replace can take a script block (PowerShell 6+) that computes each replacement from the match, $_.
1$log = 'Ember ate 4 kg, Glim ate 1 kg, then Ember ate 3 kg more'
2$log -match '(\w+) ate (\d+) kg'
3$Matches[1]
4$all = [regex]::Matches($log, '(?<dragon>\w+) ate (?<kg>\d+) kg')
5$all.Count
6$all | ForEach-Object { "$($_.Groups['dragon'].Value)=$($_.Groups['kg'].Value)" }
7'ember and glim' -replace '\b\w', { $_.Value.ToUpper() }
8[regex]::Escape('1.5 kg (approx)')True Ember 3 Ember=4 Glim=1 Ember=3 Ember And Glim 1\.5\ kg\ \(approx\)
Key takeaways
[Type]::Method()calls static .NET members;[Type]::new()creates objects;Get-Member -Staticshows what’s there.Arrays are fixed size - use
List[T]to grow,HashSet[T]for uniqueness,Queue[T]andStack[T]for order.[regex]::Matches()finds every match;-replacewith a script block computes replacements.Measure-Commandtimes a block; .NET rounds halves to even.
Lesson quiz
7 questions · pass with 5 correct · up to 50 XP
Passing this quiz completes the lesson and keeps your streak going. Questions you miss come back in review sessions later.
Practice: write PowerShell scripts
Write a script in the editor and run it for real against sample input, which is piped into your script as $input. Scripts run on PowerShell 6.2 through Try It Online (tio.run), a free public service, so these exercises avoid PowerShell 7-only syntax; your script and test input are sent there.
The feeding queue
Each input line is a command:
arrive <dragon>- the dragon joins the back of the line.feed- the dragon at the front eats: print<dragon> eats. If nobody is waiting, printtrough empty.
At the end print still waiting: <names> (front to back, comma-separated, or nobody) and different dragons fed: <n>. Use a Queue for the line and a HashSet to count distinct dragons.
- Lunchtime
- Too early
Lessons teach PowerShell 7; your script runs on PowerShell 6.2 via Try It Online (tio.run), a free public service, so stick to syntax that works there. Test input is piped into your script as $input. Your script and test input are sent to that service.
Harvest the feeding notes
Keepers write free-text notes like Ember ate 4 kg of fish, Glim ate 1 kg of moths. Every <Dragon> ate <n> kg in the input counts - there can be several per line, or none.
Print each dragon’s total as Ember: 7 kg, sorted by name, then meals: <count>. The starter uses -match, which only finds the first meal on each line.
- Busy notes
- One meal
Lessons teach PowerShell 7; your script runs on PowerShell 6.2 via Try It Online (tio.run), a free public service, so stick to syntax that works there. Test input is piped into your script as $input. Your script and test input are sent to that service.
Questions about this lesson
Stuck? Ask. Figured something out? Share it. Explaining is one of the best ways to learn.
Loading posts…