Skip to content

Commit 6fbe21f

Browse files
authored
Update TODO.md
- 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.**!
1 parent e06936a commit 6fbe21f

File tree

1 file changed

+44
-60
lines changed

1 file changed

+44
-60
lines changed

Best-Practices/TODO.md

Lines changed: 44 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
These documents are in an extremely rough state, not suitable for inclusion in the main guide yet.
22

3-
### Using The .Net Framework
3+
### Using the .NET Framework
44

55
- [Control what gets exported in a module](#control-what-gets-exported-in-a-module)
66
- [Specify when to use a Manifest for a module](#specify-when-to-use-a-manifest-for-a-module)
@@ -51,19 +51,17 @@ TODO
5151
#### Control what gets exported in a module
5252
#### Specify when to use a Manifest for a module
5353

54-
5554
#### Use RequiredAssemblies rather than Add-Type
5655
#### Use Add-Type rather than Reflection
5756
Avoid using `[System.Reflection]` to load assemblies when possible. Particularly avoid `LoadWithPartialName` (specify the full name instead).
5857

5958
#### 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?
6160

6261
#### 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.
6663

64+
TODO: Discuss: when does embedding C# code makes more sense than just compiling it every time?
6765

6866
### Performance
6967

@@ -72,10 +70,10 @@ Prefer foreach(){} over ForEach-Object
7270
Prefer .foreach and .where over cmdlets
7371
Prefer functions with process blocks over ForEach-Object
7472

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
7775
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?
7977
Discuss: Casting for creating objects
8078

8179
### Error Handling
@@ -85,7 +83,6 @@ Discuss: Avoid depending on `$?` -- why?
8583
Discuss: Never use `$Error` in scripts (always use -ErrorVariable)
8684
Discuss: Interactively, always copy $Error[0] to $e or something
8785

88-
8986
### General Design Principles
9087

9188
#### Use custom objects
@@ -103,26 +100,24 @@ Discuss: During development, always write scripts, which are automatically re-pa
103100
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:
104101

105102
```
106-
[CmdletBinding()]param()
103+
[CmdletBinding()]
104+
param()
107105
process{}
108106
end{}
109107
```
110108

111109
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
112110

113-
114111
### Include Help
115112

116113
TODO: Link to StyleGuide about formatting help
117-
Discuss: Minimum acceptable comment based 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)
118115
Discuss: Benefits of MAML help files
119116

120-
#### Always ship a about_ModuleName with modules
117+
#### Always ship an about_ModuleName with modules
121118

122119
Discuss: Other reasons to write about_topics
123120

124-
125-
126121
#### Prefer PipelineByPropertyName parameters.
127122
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.
128123

@@ -137,7 +132,7 @@ You can use aliases to map parameters to property names of objects which might b
137132
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.
138133

139134
#### 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.
141136

142137
#### Specify short aliases, but don't use them
143138
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
155150
[String]$File
156151
```
157152

158-
159153
#### Use PsCmdlet.ThrowTerminatingError rather than throw
160154
#### Use PsCmdlet.WriteError rather than Write-Error
161155
Discuss: These need example output to explain why they're better
162156
Discuss: a common template (from the Microsoft team) for throwing errors
163157

164158
```
165-
# Utility to throw an errorrecord
166-
function ThrowError
167-
{
168-
param
169-
(
170-
[parameter(Mandatory = $true)]
159+
# Utility to throw an error record
160+
function ThrowError {
161+
param(
162+
[Parameter(Mandatory = $true)]
171163
[ValidateNotNullOrEmpty()]
172164
[System.Management.Automation.PSCmdlet]
173165
$CallerPSCmdlet,
174166
175-
[parameter(Mandatory = $true)]
167+
[Parameter(Mandatory = $true)]
176168
[ValidateNotNullOrEmpty()]
177169
[System.String]
178170
$ExceptionName,
179171
180-
[parameter(Mandatory = $true)]
172+
[Parameter(Mandatory = $true)]
181173
[ValidateNotNullOrEmpty()]
182174
[System.String]
183175
$ExceptionMessage,
184176
185177
[System.Object]
186178
$ExceptionObject,
187179
188-
[parameter(Mandatory = $true)]
180+
[Parameter(Mandatory = $true)]
189181
[ValidateNotNullOrEmpty()]
190182
[System.String]
191183
$ErrorId,
192184
193-
[parameter(Mandatory = $true)]
185+
[Parameter(Mandatory = $true)]
194186
[ValidateNotNull()]
195187
[System.Management.Automation.ErrorCategory]
196188
$ErrorCategory
197189
)
198190
199-
$exception = New-Object $ExceptionName $ExceptionMessage;
191+
$exception = New-Object -TypeName $ExceptionName -ArgumentList $ExceptionMessage
200192
$errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, $ErrorId, $ErrorCategory, $ExceptionObject
201193
$CallerPSCmdlet.ThrowTerminatingError($errorRecord)
202194
}
203195
```
204196

205-
206197
#### Use SupportsShouldProcess when appropriate
207-
Discuss: when is this critical (-whatif) and optional (-confirm_
208-
Discuss: when should you call PSCmdlet.ShouldProcess vs PSCmdlet.ShouldContinue (-Force)
198+
Discuss: when is this critical (-WhatIf) and optional (-Confirm)
199+
Discuss: when should you call $PSCmdlet.ShouldProcess vs $PSCmdlet.ShouldContinue (-Force)
209200

210201
```
211202
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Medium")]
212203
param([Switch]$Force)
213204
214-
$RejectAll = $false;
215-
$ConfirmAll = $false;
205+
$RejectAll = $false
206+
$ConfirmAll = $false
216207
217-
foreach($file in ls) {
208+
foreach ($file in ls) {
218209
219-
if($PSCmdlet.ShouldProcess( "Removed the file '$($file.Name)'",
210+
if($PSCmdlet.ShouldProcess("Removed the file '$($file.Name)'",
220211
"Remove the file '$($file.Name)'?",
221-
"Removing Files" )) {
212+
"Removing Files")
213+
) {
222214
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)) {
224216
"Removing $File"
225217
226218
@@ -242,36 +234,30 @@ The problems with this have gone away with autoloading, and this is the only way
242234
#### Persisting Configuration
243235
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))
244236

245-
246237
#### Provide aliases in your modules
247238
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.
248239

249240
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.
250241

251242
Use `New-Alias ... -ErrorAction SilentlyContinue` to avoid overwriting existing aliases.
252243

253-
254-
255-
256244
### GOTCHAS
257245

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).
260248

261-
#### Beware -match and -like
249+
#### Beware of -match and -like
262250
They quietly cast objects to strings (or arrays of strings)
263251

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
267254

268255
### 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.
272257

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.
273259

274-
### You should understand the .Net underpinnings
260+
### You should understand the .NET underpinnings
275261

276262
#### AVOID appending to string in a loop
277263
##### 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
283269
* Joining an array of strings is fast
284270
* Easier to read and understand
285271

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.
286274

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).
291276

292277
Additionally, avoid using `[string]` with ParameterSets because anything can be cast to it, so PowerShell can't distinguish one parameter set from the other.
293278

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.
295280

296281
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.
297282

298-
299283
### Don't reinvent the wheel
300-
### Let's talk about Logging
301-
### Let's talk about code signing
284+
### Let's talk about logging
285+
### Let's talk about code signing

0 commit comments

Comments
 (0)