Showing posts with label enum. Show all posts
Showing posts with label enum. Show all posts

Monday, November 03, 2014

Enums in PowerShell V5

As I noted in an earlier blog post, we have begun to see the contents of what eventually is to become PowerShell V5. Amongst the cool features that are to be delivered in V5, one of my favourite is the ability to define Classes and Enums in PowerShell. I'll leave Classes for another day (and possibly multiple blog posts), but let's look at Enums.

First, what is an enum? An enum is a type that create a list of named constants. In the .NET Framework, you find the System.Enum class – which implements enums.

You can think of an enum as all the proper values that some parameter can take. For example, if you were implementing a pizza application, you might have an enum called PizzaSize, representing the available sizes of pizza and another enum called PizzaToppings that contains the valid names of all the toppings you can put on your pizza.

Of course, you could have always used the ENUM feature of .NET, and there are a lot of enums already defined.  For example, System.Net.Mail.MailPriority is an existing enum in the .NET Framework 4.5, which specifies the valid values for mail priority. This is a simple enum, with just three values: High, Low and Normal. If your code/script/function is passed a value for mail priority, it's always a good idea to validate it – and here's some PowerShell Code that can do this:

# Enums.ps1
Function Confirm-MailPriority {
param ([string] $mailpriority)
if ([enum]::IsDefined(([System.Net.Mail.MailPriority]),$mailpriority))
   {Return $true}
Else
   {return $false}
}

This function takes a string representing mail priority and returns true or false depending on whether it's valid. Here is the results of testing this function:

image

Now as you can see – the validation is case sensitive – thus 'High' is a valid mail priority, whereas 'high' isn't. You can get around that by fully spelling out the enum, and letting .NET work out the validity. Thus 'high' is invalid whereas [System.Net.Mail.MailPriority]::high (and [System.Net.Mail.MailPriority]::High) are both valid. This matters a lot if you are calling .Net method and passing enumerable values (e.g. mail priority). 

Up until Version 5 of PowerShell, you could always use the [enum] class to validate and list all the valid values of existing Enums. But what you did not have, up till now, is the ability to create a real Enum in PowerShell. Of course, you could have used Add-Type, and specified the C# code but I don't regard that as quite the same thing as defining an enum in PowerShell directly.

In V5, it's trivial to create an enum.  Just specify it's name, follow it by a script block with each enumerable value on a separate line. Like this:

Enum PizzaSize {
  Small
  Medium
  Regular
  Large
  ExtraLarge
}

Note there are no commas separating the values,just a newline. I wonder if this is a limitation of the current release and maybe additional delimiters will come in later versions. We'll have to see!

Once you define this enum, you can then use the enum and can validate it:

$size = [pizzasize]::large
[enum]::IsDefined(([pizzasize]),$size))  # True!

This is pretty cool – and lays at least part of the foundation for you to be able to develop full .NET Classes in PowerShell. 

There is at least one limitation I am aware of with Enums in PowerShell and that is that they are not persistent. Unlike the .NET Framework enums which are always available (one the relevant .NET Class Library is loaded!), you need to define enums in each script that uses them. That is, thinking about it, not such a great limitation – you just need to define your enums where you use them.

My preferred place is as as part of a module definition – just put your enums for your module in a file such as Enums.ps1 and put it into the module folder. Then, in the ScriptsToProcess key of the module's manifest, just specify Enums.ps1. That will dot-source them into your global environment and are then available for all the code you then run.

Enums are one important part of being able to develop a class in PowerShell. I'll be developing another article to describe how that works. I will probably wait till after the next drop of PowerShell V4, which is scheduled for November some time.

Technorati Tags: ,,,

Wednesday, August 05, 2009

PowerShell and .NET Framework – Similarities and Differences!

When I teach PowerShell, I point out the consistency within and across the product. When you learn something, it can be broadly used in other circumstances. This is the power of knowledge transfer – learning things once and using that knowleldge to solve other problems.

Of course, despite the consistency that’s there, there is a level of in-consistency to manage. There are things that are just not particularly intuitive – and you just have to learn those differences. There is definitely a learning curve when it comes to learning PowerShell! I’ve published what I learn as I go along – I posted an article about .NET and Powershell last December where I looked at how you access .NET from PowerShell.

With a bit of learning, via posts like mine or from Newsgroup.Forum posts, most PowerShell users can look at a bit of documentation on .NET or  WMI, and work out how to access the relevant class,  method, etc. But every so often, stuff that looks obvious isn’t! This fact (feature???) bit me over the weekend when I was working on the Get-System.Environment script that I published over on my PowerShell Scripts blog.

I was playing around with the System.Environment class, and in particular the Environment.SpecialFolder enum. Just looking at it, I felt if I can use one enum like this,

PSH [C:\foo]: [system.Enum]::GetValues([system.dayofweek])
Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday

Then I should be able to to do something like this (which as you see I can’t):

PSH [C:\foo]: [system.Enum]::GetValues([System.Environment.SpecialFolder])
Unable to find type [system.Environment.SpecialFolder]: make sure that the assembly containing this type is loaded.
At line:1 char:60
+ [system.Enum]::GetValues([system.Environment.SpecialFolder] <<<< )
    + CategoryInfo          : InvalidOperation: (system.Environment.SpecialFolder:String) [], RuntimeException
    + FullyQualifiedErrorId : TypeNotFound

Then, after some searching, I discovered may mistake!  Instead of [System.Environment.SpecialFolder], I needed to specify [System.Environment+SpecialFolder]. As shown here:

PSH [C:\foo]: [system.Enum]::GetValues([system.Environment+SpecialFolder])
Desktop
Programs
Personal
Personal
Favorites
Startup
Recent
SendTo
StartMenu
MyMusic
DesktopDirectory
MyComputer
Templates
ApplicationData
LocalApplicationData
InternetCache
Cookies
History
CommonApplicationData
System
ProgramFiles
MyPictures
CommonProgramFiles

Sure – that was obvious, NOT! But another hurdle on my learning curve!

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.