|
| 1 | +--- |
| 2 | +description: Avoid Using New-Object |
| 3 | +ms.date: 06/14/2025 |
| 4 | +ms.topic: reference |
| 5 | +title: AvoidUsingNewObject |
| 6 | +--- |
| 7 | +# AvoidUsingNewObject |
| 8 | + |
| 9 | +**Severity Level: Warning** |
| 10 | + |
| 11 | +## Description |
| 12 | + |
| 13 | +The `New-Object` cmdlet should be avoided in modern PowerShell code except when creating COM objects. PowerShell provides more efficient, readable, and idiomatic alternatives for object creation that offer better performance and cleaner syntax. |
| 14 | + |
| 15 | +This rule flags all uses of `New-Object` ***except*** when used with the `-ComObject` parameter, as COM object creation is one of the few legitimate remaining use cases for this cmdlet. |
| 16 | + |
| 17 | +## Why Avoid New-Object? |
| 18 | + |
| 19 | +### Performance Issues |
| 20 | +`New-Object` uses reflection internally, which is significantly slower than direct type instantiation or accelerated type syntax. The performance difference becomes more pronounced in loops or frequently executed code. |
| 21 | + |
| 22 | +### Readability and Maintainability |
| 23 | +Modern PowerShell syntax is more concise and easier to read than `New-Object` constructions, especially for common .NET types. |
| 24 | + |
| 25 | +### PowerShell Best Practices |
| 26 | +The PowerShell community and Microsoft recommend using native PowerShell syntax over legacy cmdlets when better alternatives exist. |
| 27 | + |
| 28 | +## Examples |
| 29 | + |
| 30 | +### Wrong |
| 31 | + |
| 32 | +```powershell |
| 33 | +# Creating .NET objects |
| 34 | +$list = New-Object System.Collections.Generic.List[string] |
| 35 | +$hashtable = New-Object System.Collections.Hashtable |
| 36 | +$stringBuilder = New-Object System.Text.StringBuilder |
| 37 | +$datetime = New-Object System.DateTime(2023, 12, 25) |
| 38 | +
|
| 39 | +# Creating custom objects |
| 40 | +$obj = New-Object PSObject -Property @{ |
| 41 | + Name = "John" |
| 42 | + Age = 30 |
| 43 | +} |
| 44 | +``` |
| 45 | + |
| 46 | +### Correct |
| 47 | + |
| 48 | +```powershell |
| 49 | +# Use accelerated type syntax for .NET objects |
| 50 | +$list = [System.Collections.Generic.List[string]]::new() |
| 51 | +$hashtable = @{} # or [hashtable]@{} |
| 52 | +$stringBuilder = [System.Text.StringBuilder]::new() |
| 53 | +$datetime = [DateTime]::new(2023, 12, 25) |
| 54 | +
|
| 55 | +# Use PSCustomObject for custom objects |
| 56 | +$obj = [PSCustomObject]@{ |
| 57 | + Name = "John" |
| 58 | + Age = 30 |
| 59 | +} |
| 60 | +
|
| 61 | +# COM objects are still acceptable with New-Object |
| 62 | +$excel = New-Object -ComObject Excel.Application |
| 63 | +$word = New-Object -ComObject Word.Application |
| 64 | +``` |
| 65 | + |
| 66 | +## Alternative Approaches |
| 67 | + |
| 68 | +### For .NET Types |
| 69 | +- **Static `new()` method**: `[TypeName]::new(parameters)` |
| 70 | +- **Type accelerators**: Use built-in shortcuts like `@{}` for hashtables |
| 71 | +- **Cast operators**: `[TypeName]$value` for type conversion |
| 72 | + |
| 73 | +### For Custom Objects |
| 74 | +- **PSCustomObject**: `[PSCustomObject]@{ Property = Value }` |
| 75 | +- **Ordered dictionaries**: `[ordered]@{ Property = Value }` |
| 76 | + |
| 77 | +### For Collections |
| 78 | +- **Array subexpression**: `@(items)` |
| 79 | +- **Hashtable literal**: `@{ Key = Value }` |
| 80 | +- **Generic collections**: `[System.Collections.Generic.List[Type]]::new()` |
| 81 | + |
| 82 | +## Performance Comparison |
| 83 | + |
| 84 | +```powershell |
| 85 | +# Slow - uses reflection |
| 86 | +Measure-Command { 1..1000 | ForEach-Object { New-Object System.Text.StringBuilder } } |
| 87 | +
|
| 88 | +# Fast - direct instantiation |
| 89 | +Measure-Command { 1..1000 | ForEach-Object { [System.Text.StringBuilder]::new() } } |
| 90 | +``` |
| 91 | + |
| 92 | +The modern syntax provides a performance improvement over `New-Object` for most common scenarios. |
| 93 | + |
| 94 | +## Exceptions |
| 95 | + |
| 96 | +The rule allows `New-Object` when used with the `-ComObject` parameter because: |
| 97 | +- COM object creation requires the `New-Object` cmdlet. |
| 98 | +- No direct PowerShell alternative exists for COM instantiation. |
| 99 | +- COM objects are external to the .NET type system. |
| 100 | + |
| 101 | +```powershell |
| 102 | +# This is acceptable |
| 103 | +$shell = New-Object -ComObject WScript.Shell |
| 104 | +$ie = New-Object -ComObject InternetExplorer.Application |
| 105 | +``` |
| 106 | + |
| 107 | +--- |
| 108 | + |
| 109 | +## Migration Guide |
| 110 | + |
| 111 | +| Old Syntax | New Syntax | |
| 112 | +|------------|------------| |
| 113 | +| **Creating a custom object**: <br> `New-Object PSObject -Property @{ Name = 'John'; Age = 30 }` | **Use PSCustomObject**: <br> `[PSCustomObject]@{ Name = 'John'; Age = 30 }` | |
| 114 | +| **Creating a hashtable**: <br> `New-Object System.Collections.Hashtable` | **Use hashtable literal**: <br> `@{}` <br> **Or explicitly cast**: <br> `[hashtable]@{}` | |
| 115 | +| **Creating a generic list**: <br> `New-Object 'System.Collections.Generic.List[string]'` | **Use static `new()` method**: <br> `[System.Collections.Generic.List[string]]::new()` | |
| 116 | +| **Creating a DateTime object**: <br> `New-Object DateTime -ArgumentList 2023, 12, 25` | **Use static `new()` method**: <br> `[DateTime]::new(2023, 12, 25)` | |
| 117 | +| **Creating a StringBuilder**: <br> `New-Object System.Text.StringBuilder` | **Use static `new()` method**: <br> `[System.Text.StringBuilder]::new()` | |
| 118 | +| **Creating a process object**: <br> `New-Object System.Diagnostics.Process` | **Use static `new()` method**: <br> `[System.Diagnostics.Process]::new()` | |
| 119 | +| **Creating a custom .NET object**: <br> `New-Object -TypeName 'Namespace.TypeName' -ArgumentList $args` | **Use static `new()` method**: <br> `[Namespace.TypeName]::new($args)` | |
| 120 | + |
| 121 | +--- |
| 122 | + |
| 123 | +### Detailed Examples |
| 124 | + |
| 125 | +#### Custom Object Creation |
| 126 | + |
| 127 | +**Old Syntax:** |
| 128 | + |
| 129 | +```powershell |
| 130 | +$obj = New-Object PSObject -Property @{ |
| 131 | + Name = 'John' |
| 132 | + Age = 30 |
| 133 | +} |
| 134 | +``` |
| 135 | + |
| 136 | +**New Syntax:** |
| 137 | + |
| 138 | +```powershell |
| 139 | +$obj = [PSCustomObject]@{ |
| 140 | + Name = 'John' |
| 141 | + Age = 30 |
| 142 | +} |
| 143 | +``` |
| 144 | + |
| 145 | +#### Hashtable Creation |
| 146 | + |
| 147 | +**Old Syntax:** |
| 148 | + |
| 149 | +```powershell |
| 150 | +$hashtable = New-Object System.Collections.Hashtable |
| 151 | +$hashtable.Add('Key', 'Value') |
| 152 | +``` |
| 153 | + |
| 154 | +**New Syntax:** |
| 155 | + |
| 156 | +```powershell |
| 157 | +$hashtable = @{ |
| 158 | + Key = 'Value' |
| 159 | +} |
| 160 | +``` |
| 161 | + |
| 162 | +Or explicitly cast: |
| 163 | + |
| 164 | +```powershell |
| 165 | +$hashtable = [hashtable]@{ |
| 166 | + Key = 'Value' |
| 167 | +} |
| 168 | +``` |
| 169 | + |
| 170 | +#### Generic List Creation |
| 171 | + |
| 172 | +**Old Syntax:** |
| 173 | + |
| 174 | +```powershell |
| 175 | +$list = New-Object 'System.Collections.Generic.List[string]' |
| 176 | +$list.Add('Item1') |
| 177 | +$list.Add('Item2') |
| 178 | +``` |
| 179 | + |
| 180 | +**New Syntax:** |
| 181 | + |
| 182 | +```powershell |
| 183 | +$list = [System.Collections.Generic.List[string]]::new() |
| 184 | +$list.Add('Item1') |
| 185 | +$list.Add('Item2') |
| 186 | +``` |
| 187 | + |
| 188 | +#### DateTime Object Creation |
| 189 | + |
| 190 | +**Old Syntax:** |
| 191 | + |
| 192 | +```powershell |
| 193 | +$date = New-Object DateTime -ArgumentList 2023, 12, 25 |
| 194 | +``` |
| 195 | + |
| 196 | +**New Syntax:** |
| 197 | + |
| 198 | +```powershell |
| 199 | +$date = [DateTime]::new(2023, 12, 25) |
| 200 | +``` |
| 201 | + |
| 202 | +#### StringBuilder Creation |
| 203 | + |
| 204 | +**Old Syntax:** |
| 205 | + |
| 206 | +```powershell |
| 207 | +$stringBuilder = New-Object System.Text.StringBuilder |
| 208 | +$stringBuilder.Append('Hello') |
| 209 | +``` |
| 210 | + |
| 211 | +**New Syntax:** |
| 212 | + |
| 213 | +```powershell |
| 214 | +$stringBuilder = [System.Text.StringBuilder]::new() |
| 215 | +$stringBuilder.Append('Hello') |
| 216 | +``` |
| 217 | + |
| 218 | +#### Custom .NET Object Creation |
| 219 | + |
| 220 | +**Old Syntax:** |
| 221 | + |
| 222 | +```powershell |
| 223 | +$customObject = New-Object -TypeName 'Namespace.TypeName' -ArgumentList $arg1, $arg2 |
| 224 | +``` |
| 225 | + |
| 226 | +**New Syntax:** |
| 227 | + |
| 228 | +```powershell |
| 229 | +$customObject = [Namespace.TypeName]::new($arg1, $arg2) |
| 230 | +``` |
| 231 | + |
| 232 | +#### Process Object Creation |
| 233 | + |
| 234 | +**Old Syntax:** |
| 235 | + |
| 236 | +```powershell |
| 237 | +$process = New-Object System.Diagnostics.Process |
| 238 | +``` |
| 239 | + |
| 240 | +**New Syntax:** |
| 241 | + |
| 242 | +```powershell |
| 243 | +$process = [System.Diagnostics.Process]::new() |
| 244 | +``` |
| 245 | + |
| 246 | +--- |
| 247 | +## Related Links |
| 248 | + |
| 249 | +- [New-Object][01] |
| 250 | +- [PowerShell scripting performance considerations][02] |
| 251 | +- [Creating .NET and COM objects][03] |
| 252 | + |
| 253 | +<!-- link references --> |
| 254 | +[01]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/new-object |
| 255 | +[02]: https://learn.microsoft.com/en-us/powershell/scripting/dev-cross-plat/performance/script-authoring-considerations |
| 256 | +[03]: https://learn.microsoft.com/en-us/powershell/scripting/samples/creating-.net-and-com-objects--new-object- |
0 commit comments