Oct
19
Written by:
Joe Brinkman
Friday, October 19, 2007 9:13 AM
In my last post Getting a Username/Password in PowerShell (cross posted to PowerShellCommunity.org), I was asked why not just use the Add-Member cmdlet. Having been doing software development for my entire adult life, this is not the first time this question has come up. Ok. Maybe not those exact words, but something very similar - why use code X when code Y does the same thing. Where I come from there is really only one response to this - look at both code alternatives and determine which one is the most efficient at doing the job without also becoming a maintenance problem. So lets take a peek under the hood a bit so we can see why I chose PSObject.Members.Add over using the Add-Member cmdlet.
Scripting is code - except on the command line
When working in PowerShell I try to keep a clear understanding of my usage scenario. If I am writing something intended to be typed on the command line then I tend to favor a more script oriented approach where I make heavy use of piping one command into another and where cmdlets provide all of the heavy lifting. However, when I sit down to write a function, I am coding and I tend to favor a much more expanded and explicit coding style using variables and objects. This makes it easier to maintain the code and to understand what exactly I am trying to accomplish.
Know what is happening under the cover
Once I know what coding style I will be using then I need to evaluate the effectiveness and efficiency of the coding alternatives. Often this means you will need to understand what is happening inside the framework. One of the reasons that I am excited about the announcement that .Net is going to provide Source code is that I will finally be able to look at the source code with full commenting. Until that happens though, I still have Reflector and I use it very heavily.
In this case the question is: Why use Members.Add instead of using Add-Member? Lets look at what is going on in Add-Member and you'll see why.
Microsoft.PowerShell.Commands.AddMemberCommand.ProcessRecord() is the relevant method that gets executed when we run the Add-Member cmdlet. There is some basic error checking to make sure that certain cmdlet parameters were set - like membertype.
Next we have a giant case statement:
Select Case Me.memberType
Case PSMemberTypes.AliasProperty
member = Me.GetAliasProperty
Case PSMemberTypes.CodeProperty
member = Me.GetCodeProperty
Case PSMemberTypes.NoteProperty
member = Me.GetNoteProperty
Case PSMemberTypes.ScriptProperty
member = Me.GetScriptProperty
Case PSMemberTypes.ScriptMethod
member = Me.GetScriptMethod
Case PSMemberTypes.MemberSet
member = Me.GetMemberSet
Case PSMemberTypes.PropertySet
member = Me.GetPropertySet
Case PSMemberTypes.CodeMethod
member = Me.GetCodeMethod
Case Else
'Deal with error...
End Select
This case statement doesn't provide any real value to our function since I already know what type of members I wish to add, I don't need the cmdlet to make a conversion from my "string" to a root class type. Even once the membertype is determined, the cmdlet calls another function (GetNoteProperty in our case) which we see below:
Private Function GetNoteProperty() As PSMemberInfo
Me.EnsureValue1HasBeenSpecified
Me.EnsureValue2HasNotBeenSpecified
Return New PSNoteProperty(Me.memberName, Me.value1)
End Function
So in essence, this big case statement is doing exactly the same thing as my code, except with a lot more overhead. It is creating a new PSNoteProperty. Now lets look at the next part of the ProcessRecord method.
Dim info2 As PSMemberInfo = Me.inputObject.Members.Item(member.Name)
If (Not info2 Is Nothing) Then
If Not Me.force Then
MyBase.WriteError("...")
Else
If info2.IsInstance Then
Me.inputObject.Members.Remove(member.Name)
goto Label_0201
End If
MyBase.WriteError("...")
End If
Return
End If
Me.inputObject.Members.Add(member)
So we see that beyond a bunch of extra error handling to deal with members that already exist, this code just calls Members.Add().
At this point I think it is safe to say that my original code and the Add-Member cmdlet are performing exactly the same function. However, with the cmdlet there is a performance penalty to be paid for parsing all of the strings passed to the cmdlet and converting them to types. Determining how to act based on some of the parameters (like -force). Also, I think that my code is just as readable as using the cmdlet. Ultimately though, Add-Member is just sugar coating for adding a PSMemberInfo object to a PSObject.Members collection.
So, the next time you have a question about whether to use a cmdlet or whether to use "code", pull out reflector and see what is really happening under the hood.
1 comment(s) so far...
Re: Why not just use Add-Member
I didn't expect another great post as an answer to my comment. :-) I didn't ask why you had used members.add instead of add-member. Just gave an example how can we do the same thing with another command. That's all. I'm not a developer, and I don't use Reflector very often. Maybe I should. Thanks for this detailed explanation.
By aleksandar on
Thursday, October 18, 2007 5:58 PM
|