You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- Change '.Net' to '.NET' to keep it consistent.
- Remove several spurious blank lines.
- Change 'PowerShell Classes' to 'PowerShell classes'.
- Change "'s" to "or".
- Change 'makes sense more sense' to 'makes more sense'.
- Move 'param()' in '[CmdletBinding()]param()' to the next line.
- Change 'comment based help' to 'comment-based help'.
- Change 'a about_ModuleName' to 'an about_ModuleName'.
- Change 'that pases parameters positionally' to 'that passes parameters positionally'.
- Change 'PsCmdlet.ThrowTerminatingError' to '$PSCmdlet.ThrowTerminatingError'.
- Change 'PsCmdlet.WriteError' to '$PSCmdlet.WriteError'.
- Change inconsistencies with the styling guide/conventions in the example code for function ThrowError:
- Change typo 'errorrecord' to 'error record'.
- Move brace after 'function ThrowError' to the same line.
- Move parenthesis after 'param' to the same line.
- Change 'parameter' to 'Parameter'.
- Change '$exception = New-Object $ExceptionName $ExceptionMessage;' to '$exception = New-Object -TypeName $ExceptionName -ArgumentList $ExceptionMessage' (so using named parameters instead of positional parameters and removing the unnecessary semicolon at the end).
- Change 'Discuss: when is this critical (-whatif) and optional (-confirm_' to 'Discuss: when is this critical (-WhatIf) and optional (-Confirm)'.
- Change 'Discuss: when should you call PSCmdlet.ShouldProcess vs PSCmdlet.ShouldContinue (-Force)' to 'Discuss: when should you call $PSCmdlet.ShouldProcess vs $PSCmdlet.ShouldContinue (-Force)'.
- Change more inconsistencies in example code:
- Remove unnecessary semicolons at the end of the line.
- Add space after 'foreach'.
- Remove inconsistent spacing.
- Add space after 'if'.
- Change more small typos.
P.S.: Make '$errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, $ErrorId, $ErrorCategory, $ExceptionObject' code that actually works! This code will generate the error **New-Object: Cannot bind argument to parameter 'TypeName' because it is null.**!
Copy file name to clipboardExpand all lines: Best-Practices/TODO.md
+44-60Lines changed: 44 additions & 60 deletions
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,6 @@
1
1
These documents are in an extremely rough state, not suitable for inclusion in the main guide yet.
2
2
3
-
### Using The .Net Framework
3
+
### Using the .NET Framework
4
4
5
5
-[Control what gets exported in a module](#control-what-gets-exported-in-a-module)
6
6
-[Specify when to use a Manifest for a module](#specify-when-to-use-a-manifest-for-a-module)
@@ -51,19 +51,17 @@ TODO
51
51
#### Control what gets exported in a module
52
52
#### Specify when to use a Manifest for a module
53
53
54
-
55
54
#### Use RequiredAssemblies rather than Add-Type
56
55
#### Use Add-Type rather than Reflection
57
56
Avoid using `[System.Reflection]` to load assemblies when possible. Particularly avoid `LoadWithPartialName` (specify the full name instead).
58
57
59
58
#### Use Add-Type for small classes or PInvoke calls
60
-
TODO: Is this better than PowerShell Classes, for compatibility?
59
+
TODO: Is this better than PowerShell classes, for compatibility?
61
60
62
61
#### Prefer shipping binaries over large compilations
63
-
With PowerShell 5, security is tighter, and compiling code in memory will be frowned upon. Now that we have PowerShellGet and the PowerShell Gallery, there are few reasons 's no reason to avoid binaries.
64
-
65
-
TODO: Discuss: when does embedding C# code makes sense more sense than just compiling it every time?
62
+
With PowerShell 5, security is tighter, and compiling code in memory will be frowned upon. Now that we have PowerShellGet and the PowerShell Gallery, there are few reasons or no reason to avoid binaries.
66
63
64
+
TODO: Discuss: when does embedding C# code makes more sense than just compiling it every time?
67
65
68
66
### Performance
69
67
@@ -72,10 +70,10 @@ Prefer foreach(){} over ForEach-Object
72
70
Prefer .foreach and .where over cmdlets
73
71
Prefer functions with process blocks over ForEach-Object
74
72
75
-
#### Know when to use .Net
76
-
Prefer writing wrapper functions to just calling .Net APIs
73
+
#### Know when to use .NET
74
+
Prefer writing wrapper functions to just calling .NET APIs
77
75
Discuss: System.IO file loading vs Get-Content (large files)
78
-
Discuss: Other examples of .Net API calls that are clearly faster?
76
+
Discuss: Other examples of .NET API calls that are clearly faster?
Discuss: Never use `$Error` in scripts (always use -ErrorVariable)
86
84
Discuss: Interactively, always copy $Error[0] to $e or something
87
85
88
-
89
86
### General Design Principles
90
87
91
88
#### Use custom objects
@@ -103,26 +100,24 @@ Discuss: During development, always write scripts, which are automatically re-pa
103
100
This is in the Style Guide too, but we should discuss it in more depth here, and link to it from the Style Guide. Scripts should start life as something like this:
104
101
105
102
```
106
-
[CmdletBinding()]param()
103
+
[CmdletBinding()]
104
+
param()
107
105
process{}
108
106
end{}
109
107
```
110
108
111
109
You can always ignore one of the blocks, and add parameters and such, but you should never write a script without CmdletBinding, and you should never write one without at least considering making it take pipeline input
112
110
113
-
114
111
### Include Help
115
112
116
113
TODO: Link to StyleGuide about formatting help
117
-
Discuss: Minimum acceptable commentbased help: Synopsis, Parameters, and an example for each parameter set (plus pipeline examples if you can contrive one)
114
+
Discuss: Minimum acceptable comment-based help: Synopsis, Parameters, and an example for each parameter set (plus pipeline examples if you can contrive one)
118
115
Discuss: Benefits of MAML help files
119
116
120
-
#### Always ship a about_ModuleName with modules
117
+
#### Always ship an about_ModuleName with modules
121
118
122
119
Discuss: Other reasons to write about_topics
123
120
124
-
125
-
126
121
#### Prefer PipelineByPropertyName parameters.
127
122
Discuss: This allows the most flexibility: piping objects and using scriptblocks to shape it for parameters. Unless you absolutely need to write a `begin` block and use this parameter in it, you probably should accept it on the pipeline.
128
123
@@ -137,7 +132,7 @@ You can use aliases to map parameters to property names of objects which might b
137
132
Particularly when splatting PSBoundParameters to the next function, if that function isn't `[CmdletBinding()]` (it should be!) you must remember to strip the common parameters if they're present.
138
133
139
134
#### Specify positional parameters, but don't use them
140
-
When writing at the command line, positional parameters are a blessing, but they can be confusing for future readers. You should always expose your parameters positionally when it makes sense, but you should rarely share a script that pases parameters positionally.
135
+
When writing at the command line, positional parameters are a blessing, but they can be confusing for future readers. You should always expose your parameters positionally when it makes sense, but you should rarely share a script that passes parameters positionally.
141
136
142
137
#### Specify short aliases, but don't use them
143
138
Again, for the sake of typing, it's particularly useful if you can provide two-letter aliases for each of your parameters such that every parameter has a two-letter or less name which is unique.
@@ -155,72 +150,69 @@ ValueFromPipelineByPropertyName = $true, HelpText = 'The name of the file to rea
155
150
[String]$File
156
151
```
157
152
158
-
159
153
#### Use PsCmdlet.ThrowTerminatingError rather than throw
160
154
#### Use PsCmdlet.WriteError rather than Write-Error
161
155
Discuss: These need example output to explain why they're better
162
156
Discuss: a common template (from the Microsoft team) for throwing errors
if($PSCmdlet.ShouldProcess("Removed the file '$($file.Name)'",
210
+
if($PSCmdlet.ShouldProcess("Removed the file '$($file.Name)'",
220
211
"Remove the file '$($file.Name)'?",
221
-
"Removing Files" )) {
212
+
"Removing Files")
213
+
) {
222
214
223
-
if($Force -Or $PSCmdlet.ShouldContinue("Are you REALLY sure you want to remove '$($file.Name)'?", "Removing '$($file.Name)'", [ref]$ConfirmAll, [ref]$RejectAll)) {
215
+
if($Force -Or $PSCmdlet.ShouldContinue("Are you REALLY sure you want to remove '$($file.Name)'?", "Removing '$($file.Name)'", [ref]$ConfirmAll, [ref]$RejectAll)) {
224
216
"Removing $File"
225
217
226
218
@@ -242,36 +234,30 @@ The problems with this have gone away with autoloading, and this is the only way
242
234
#### Persisting Configuration
243
235
My choice: Configuration module. Otherwise, use clixml (or XAML) to persist to AppData (TEST: you shouldn't store configuration in your module folder, as it may not survive upgrades (in PowerShell 3 & 4 there was no side-by-side loading))
244
236
245
-
246
237
#### Provide aliases in your modules
247
238
You should feel free to create and use aliases within your modules. In some cases, you can even improve readability by using an alias without the verb, or shortening command names.
248
239
249
240
For exported aliases, follow the guidance of Microsoft ("ip" for import, "s" for set, "g" for get, "r" for remove, etc.), make up something for your nouns.
250
241
251
242
Use `New-Alias ... -ErrorAction SilentlyContinue` to avoid overwriting existing aliases.
252
243
253
-
254
-
255
-
256
244
### GOTCHAS
257
245
258
-
#### Beware string concatenation with +
259
-
You should always wrap this with parentheses, because otherwise it can break (e.g. when passing a string as a parameter.
246
+
#### Beware of string concatenation with +
247
+
You should always wrap this with parentheses, because otherwise it can break (e.g., when passing a string as a parameter).
260
248
261
-
#### Beware -match and -like
249
+
#### Beware of -match and -like
262
250
They quietly cast objects to strings (or arrays of strings)
263
251
264
-
#### Beware -contains and -in
265
-
They work on ARRAYS not strings
266
-
252
+
#### Beware of -contains and -in
253
+
They work on ARRAYS, not strings
267
254
268
255
### Use Language Features
269
-
When writing scripts (as opposed to at the command line), you should almost always choose language features over cmdlets. This includes using if instead of where-object, foreach instead of ForEach-Object, etc.
270
-
271
-
The language features are always faster, and almost always more readable. Of course, there are always exceptions, and one exception to this rule is when using foreach will force you to collect a lot of items into an array instead of iterating them as they stream through a pipleine.
256
+
When writing scripts (as opposed to at the command line), you should almost always choose language features over cmdlets. This includes using `if` instead of `Where-Object`, `foreach` instead of `ForEach-Object`, etc.
272
257
258
+
The language features are always faster, and almost always more readable. Of course, there are always exceptions, and one exception to this rule is when using `foreach` will force you to collect a lot of items into an array instead of iterating them as they stream through a pipeline.
273
259
274
-
### You should understand the .Net underpinnings
260
+
### You should understand the .NET underpinnings
275
261
276
262
#### AVOID appending to string in a loop
277
263
##### INSTEAD assign output from the loop using $OFS
@@ -283,19 +269,17 @@ The language features are always faster, and almost always more readable. Of cou
283
269
* Joining an array of strings is fast
284
270
* Easier to read and understand
285
271
272
+
### Strongly typed parameters
273
+
Although PowerShell is a dynamic language, we can specify types, and in parameters, it's particularly useful because it hints to users what they can pass to your command.
286
274
287
-
### Strongly type parameters
288
-
Although PowerShell is a dynamic language, we have can specify types, and in Parameters, it's particularly useful because it hints to users what they can pass to your command.
289
-
290
-
Strong types on parameters is also crucial because it's a user-input point. Strong types can help you avoid script injection and various other problems with user inputs, and will allow failures to happen as early as possible (even before your command is called).
275
+
Strong types on parameters is also crucial because it's a user-input point. Strong types can help you avoid script injection and various other problems with user inputs, and will allow failures to happen as early as possible (even before your command is called).
291
276
292
277
Additionally, avoid using `[string]` with ParameterSets because anything can be cast to it, so PowerShell can't distinguish one parameter set from the other.
293
278
294
-
When passing on parameters to another command, you should be _at least_ as strongly typed as the other command, to avoid casting exceptions within your script.
279
+
When passing on parameters to another command, they should be _at least_ as strongly typed as the other command, to avoid casting exceptions within your script.
295
280
296
281
One notable exception is when you could accept more than one type. In PowerShell you can specify parameter set overloads, but you can't change the type of a parameter.
0 commit comments