Friday, December 26, 2008

Enums, Enum values and PowerShell

 

I’ve been reading Jeffrey Snover's discussions on ENUMs over on the PowerShell Team blog (here, and here). I knew most of this stuff and have  been playing with Enums and PowerShell for a while. I’ve created a bunch of sample scripts I’ve uploaded both to the MDSN Wiki and to my PowerShell Scripts blog. This morning, I stumbled upon an interesting use of Enums – parsing strings into Integer values, as shown on the MSDN Library at http://msdn.microsoft.com/en-us/library/system.globalization.numberstyles.aspx.

Effectively want I wanted to do was to convert the following C# code to PowerShell:

  1. // Parse the string, allowing a leading sign, and ignoring leading and trailing white spaces. 
  2. num = "    -45   "
  3. val = int.Parse(num, NumberStyles.AllowLeadingSign |  
  4.      NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite); 
  5. Console.WriteLine("'{0}' parsed to an int is '{1}'.", num, val); 

Initially, I could not work out how to use the enums  (In System.Globalization.NumberStyles). Thanks to Shay Levy, it turns out to be remarkably easy (although not quite as succinct as in C'#). Basically, in true PowerShell style, every enum value is just an object. Objects have properties, and one property of an enum value is “value__” as demonstrated here:

PSH [D:\foo]: [System.Globalization.NumberStyles]::AllowLeadingSign
AllowLeadingSign
PSH [D:\foo]: [System.Globalization.NumberStyles]::AllowLeadingSign | gm

   TypeName: System.Globalization.NumberStyles

Name        MemberType   Definition
----        ----------   ----------
CompareTo   Method       System.Int32 CompareTo(Object target)
Equals      Method       System.Boolean Equals(Object obj)
GetHashCode Method       System.Int32 GetHashCode()
GetType     Method       System.Type GetType()
GetTypeCode Method       System.TypeCode GetTypeCode()
ToString    Method       System.String ToString(), System.String ToString(String format, IFormatPr...
value__     Property     System.Int32 value__ {get;set;}
MSDN        ScriptMethod System.Object MSDN();

PSH [D:\foo]: [System.Globalization.NumberStyles]::AllowLeadingSign.value__
4
PSH [D:\foo]:

As you can see from this output, the enum [System.Globalization.NumberStyles]::AllowLeadingSign displays normally the value “AllowLeadingSign” , but if you use the .value__ property on this enum value, you get back a number – in this case a decimal 4 (0x0004). You can then combine these as follows to convert the above C# Code into PowerShell:

  1. $num = " -45 "
  2.   $val = [system.int32]::Parse($num, 
  3.                [System.Globalization.NumberStyles]::AllowLeadingSign.value__ + 
  4.                [System.Globalization.NumberStyles]::AllowLeadingWhite.value__ + 
  5.                [System.Globalization.NumberStyles]::AllowTrailingWhite.value__) 
  6. "'{0}' parsed to an int is '{1}'." -f $num, $val 

Thanks to Shay Levy for his post in the PowerShell newsgroup for helping me to work this out. FWIW: you can the results of this up on the MSDN Library and on my PowerShell Scripts blog.

 

3 comments:

Anonymous said...

In PowerShell, you can typically just use the string for an enum, like: "AllowLeadingSign" instead of [System.Globalization.NumberStyles]::AllowLeadingSign

Additionally, you can combine enum values as a comma-separated string, instead of using -bor (bitwise or, which is what you had in the C# example) or addition (which works sometimes, but is not the correct way to combine flag enums).

In the case of Int.Parse, you have to look out for the overloads of the function, so you must explicitly cast your string to the enum type:

[int]::parse( " -54 ", [Globalization.NumberStyles]"AllowLeadingSign, AllowLeadingWhite, AllowTrailingWhite" )

Isn't that easier?

Anonymous said...

After I posted a moment ago, I realized there's an easier way, in this case. Looking at that post by Jeffrey about enums, you can use [Enum]::GetValues like this:

[enum]::GetValues([System.Globalization.NumberStyles]) | %{ "{0,3} {1}" -f $([int]$_),$_ }

And I also noticed that if you just type:

[System.Globalization.NumberStyles]"AllowLeadingSign,AllowLeadingWhite,AllowTrailingWhite"

The console returns: Integer

It turns out that [System.Globalization.NumberStyles]::Integer is equivalent to that manual combination :)

jormis said...

Additionally, some perhaps "further" information can be had by doing [System.Globalization.NumberStyles].GetFields() which tells you well err more kind of information regarding the enum fields.