In part 1, we began exploring how cmdlets internally rely on .NET libraries and how you can access these libraries directly. As we discovered, most libraries offer much more functionality than what is exposed by cmdlets.
In this part, well explore the differences between types and libraries, and examine "overloads"what they are and why they provide you with greater control. As always, well use a practical example that is required in many scripts: mathematical operations and rounding.
Libraries and Types
Lets first clarify the terminology. In part 1, we discussed .NET libraries. However, when you examine codesuch as [System.IO.Path]these libraries appear to be types. So, what are they? Libraries or types?
The answer is: both. In essence, they are classes. Each class can function as a type (for data storage), or as a library (for providing additional commands).
Well-Known Types
[int] is a well-known type and represents numbers:
PS C:\> [int]$a = 12.564
PS C:\> $a
13
As you can see, the number is stored, and the type ensures that the data is stored in a specific form. In this case, the floating-point number 12.564 was rounded by [int] to a whole number.
[int] can also be viewed as a library and supports the same tricks you saw in part 1, so you can access its properties and methods using a double colon:
PS C:\> [int]::MaxValue
2147483647
PS C:\> [int]::MinValue
-2147483648
Its properties, MaxValue and MinValue, conveniently help you look up the value range supported by this type, but the type doesnt provide much else. It is designed for data storage.
More Control with Libraries
Other types cannot store data. Instead, they provide commands (called methods) for a given task. [Math], for example, provides all the sophisticated mathematical methods you may require. The [Math] type acts more like a library of commands. You can use the trick from part 1 to list all the mathematical methods found in this library:
PS C:\> [Math] | Get-Member -Static
TypeName: System.Math
Name MemberType Definition
---- ---------- ----------
Abs Method static sbyte Abs(sbyte value), static int16 Abs(int16 value), static int Abs(int value), static long Abs(long value), static decimal Abs(decimal val...
Acos Method static double Acos(double d)
Asin Method static double Asin(double d)
Atan Method static double Atan(double d)
Atan2 Method static double Atan2(double y, double x)
BigMul Method static long BigMul(int a, int b)
Ceiling Method static decimal Ceiling(decimal d), static double Ceiling(double a)
Cos Method static double Cos(double d)
Cosh Method static double Cosh(double value)
DivRem Method static int DivRem(int a, int b, [ref] int result), static long DivRem(long a, long b, [ref] long result)
Equals Method static bool Equals(System.Object objA, System.Object objB)
Exp Method static double Exp(double d)
Floor Method static decimal Floor(decimal d), static double Floor(double d)
IEEERemainder Method static double IEEERemainder(double x, double y)
Log Method static double Log(double a, double newBase), static double Log(double d)
Log10 Method static double Log10(double d)
Max Method static sbyte Max(sbyte val1, sbyte val2), static byte Max(byte val1, byte val2), static int16 Max(int16 val1, int16 val2), static uint16 Max(uint16 ...
Min Method static sbyte Min(sbyte val1, sbyte val2), static byte Min(byte val1, byte val2), static int16 Min(int16 val1, int16 val2), static uint16 Min(uint16 ...
Pow Method static double Pow(double x, double y)
ReferenceEquals Method static bool ReferenceEquals(System.Object objA, System.Object objB)
Round Method static double Round(double value, int digits), static double Round(double value, System.MidpointRounding mode), static double Round(double value, in...
Sign Method static int Sign(sbyte value), static int Sign(int16 value), static int Sign(int value), static int Sign(long value), static int Sign(float value), s...
Sin Method static double Sin(double a)
Sinh Method static double Sinh(double value)
Sqrt Method static double Sqrt(double d)
Tan Method static double Tan(double a)
Tanh Method static double Tanh(double value)
Truncate Method static decimal Truncate(decimal d), static double Truncate(double d)
E Property static double E {get;}
PI Property static double PI {get;}
As always, with this knowledge, you can execute much more granular control. Lets explore how [Math] can help you round values more effectively:
PS C:\> [int]12.564
13
PS C:\> [Math]::Round(12.564)
13
PS C:\> [Math]::Round(12.564,2)
12,56
Converting a float to an int always rounds to a whole number. After all, thats what [int] requires, and when you round numbers this way, you are simply (ab)using the nature of [int].
Behind the scenes, [int] is calling a command to round numbers. When you do the same and directly call the rounding method, you initially get the same result (a whole number). However, now you have access to all overloads (alternative rounding methods). By submitting a second argument, you can control the number of digits you want to receive. In the example, the final call rounded to two digits.
Understanding Overloads
As in part 1, you can view the overload definitions (alternative method calls) by omitting the parentheses:
- In your favorite IDE, enclose the library name in square brackets and add two colons: [System.IO.Path]::. IntelliSense will now show the available properties and methods. You may need to press CTRL+SPACE to invoke IntelliSense.
- Once you have selected a method, execute it without parentheses. This will display the method overloads (its help).
PS C:\> [Math]::Round
OverloadDefinitions
-------------------
static double Round(double value, int digits)
static double Round(double value, System.MidpointRounding mode)
static double Round(double value, int digits, System.MidpointRounding mode)
static decimal Round(decimal d)
static decimal Round(decimal d, int decimals)
static decimal Round(decimal d, System.MidpointRounding mode)
static decimal Round(decimal d, int decimals, System.MidpointRounding mode)
static double Round(double a)
With Round(), youll see eight different ways to call the method. Keep in mind that overload definitions are intended for developers, not IT pros, so they often differ only in types. The functionally distinct calls are as follows:
OverloadDefinitions
-------------------
static double Round(double value)
static double Round(double value, int digits)
static double Round(double value, System.MidpointRounding mode)
static double Round(double value, int digits, System.MidpointRounding mode)
Here´s how the overload definitions can be deciphered by mere mortalslets pick one of the overloads:
static double Round(double value, int digits, System.MidpointRounding mode)
- static: indicates that the method is directly implemented by the type
- double: the method Round() returns the data type double (so even if you round with no decimals, you get back a double and not an int)
- Round: this is the name of the method
- double value: the first argument, described by two words: the first argument must be a double type and represents the original value that you want to round
- Comma (,): separates the arguments
- int digits: the second argument, again described by two words: the second argument must be an int type and represents the number of digits you want to receive
- Comma (,): separates the arguments
- MidpointRounding mode: the third argument, again described by two words: the second argument must be a System.MidpointRounding type and defines where the pivot point for rounding up or down occurs
Picking The Best Overload
Overloads exist so you dont have to use all the possible arguments if you dont need them. Here, we are using three different overloads, and each additional overload accepts more arguments for more control:
$number = 12.545
[Math]::Round($number)
# -> 13
[Math]::Round($number, 2)
# -> 12.54
[Math]::Round($number, 2, [System.MidpointRounding]::ToEven)
# -> 12.54
[Math]::Round($number, 2, [System.MidpointRounding]::AwayFromZero)
# -> 12.55
Practical Use Case
Lets assume you want to report the free space on your hard drive(s). Here are a few ways to do it.
Without any knowledge of rounding, this could be your solution:
Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DriveType=3' |
ForEach-Object {
[PSCustomObject]@{
Drive = $_.DeviceID
'Size (MB)' = $_.Size/1MB
'Free Space (MB)' = $_.FreeSpace/1MB
PercentFree = $_.FreeSpace * 100 / $_.Size
}
}
Drive Size (MB) Free Space (MB) PercentFree
----- --------- --------------- -----------
C: 960550,99609375 55243,57421875 5,75123803352531
By abusing the [int] type, you can round to the whole number:
[Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DriveType=3' |
ForEach-Object {
[PSCustomObject]@{
Drive = $_.DeviceID
'Size (MB)' = $_.Size/1MB -as [int]
'Free Space (MB)' = $_.FreeSpace/1MB -as [int]
PercentFree = $_.FreeSpace * 100 / $_.Size -as [int]
}
}
Drive Size (MB) Free Space (MB) PercentFree
----- --------- --------------- -----------
C: 960551 55239 6
With [Math] and Round(), you can specify the number of digits for the result that works best for you:
Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DriveType=3' |
ForEach-Object {
[PSCustomObject]@{
Drive = $_.DeviceID
'Size (MB)' = [Math]::Round( ($_.Size/1MB), 1)
'Free Space (MB)' = [Math]::Round( ($_.FreeSpace/1MB), 2)
PercentFree = [Math]::Round( ($_.FreeSpace * 100 / $_.Size), 1)
}
}
Drive Size (MB) Free Space (MB) PercentFree
----- --------- --------------- -----------
C: 960551 55238,11 5,8
When you look closely, youll see that the total size is reported as a number with no digits, even though we asked for one digit. Thats because Round() always returns numbers, not formatted strings.
If the digit happens to be "0," then PowerShells console formatter wont display it:
PS C:\> 1.0
1
Just for completeness, you could also use the -f operator to round values. Because this operator always returns strings, the digit would now always be visible, even if it happens to be "0."
However, since the values are now strings, this would be useful for reporting purposes only. Anyone receiving the data would have a hard time evaluating the string numbers further:
Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'DriveType=3' |
ForEach-Object {
[PSCustomObject]@{
Drive = $_.DeviceID
'Size (MB)' = '{0:n1}' -f ($_.Size/1MB)
'Free Space (MB)' = '{0:n2}' -f ($_.FreeSpace/1MB)
PercentFree = '{0:n1}' -f ($_.FreeSpace * 100 / $_.Size)
}
}
Drive Size (MB) Free Space (MB) PercentFree
----- --------- --------------- -----------
C: 960.551,0 55.253,95 5,8
Your ultimate PowerShell Cheat Sheet
Unleash the full potential of PowerShell with our handy poster. Whether you're a beginner or a seasoned pro, this cheat sheet is designed to be your go-to resource for the most important and commonly used cmdlets.The poster is available for download and in paper form.
Related links
- ScriptRunner ActionPacks provide ready-to-use PowerShell scripts.
- Try out ScriptRunner here
- ScriptRunner: Book a demo with our product experts