I tried these assignment statements below but none of them works.
$max_value = Integer.MaxValue
$max_value = Int32.MaxValue
$max_value = int.MaxValue
Santiago Squarzon provided the crucial pointer:
# Equivalent of the following C# code: int.MaxValue or System.Int32.MaxValue
[int]::MaxValue
[int] is a PowerShell type literal that refers to .NET's System.Int32 type, i.e. a signed, 32-bit integer.
:: is PowerShell's operator for accessing static type members (whereas . is used for instance members, as in C#).
Aside from requiring that literal type names be enclosed in [...], PowerShell's type literals have some non-obvious features:
Unlike in C#, case does not matter in PowerShell type literals. E.g., [system.io.fileinfo] can be used to refer to the System.IO.FileInfo type.
Analogous to C# providing short names for frequently used types - such as int for System.Int32 and string for System.String - PowerShell defines a range of short type names called type accelerators, listed in the conceptual about_Type_Accelerators help topic.
Case in point: as in C#, [int] refers to System.Int32, which you may therefore also reference as [System.Int32].
You can invoke .FullName on any given type literal to see the target type's full (namespace-qualified) name; e.g, [int].FullName returns string System.Int32. This relies on the fact that any type literal is itself an instance of the System.Type type.
Irrespective of type accelerators, you always have the option of omitting the System. component of a type's name, so that you may refer to System.Linq.Enumerable as just [Linq.Enumerable] instead of having to spell out [System.Linq.Enumerable], for instance.
In PowerShell version 5 and above (including PowerShell (Core) 7+), you may use a using namespace statement, which, analogous to C#'s using statement, allows you to refer to the types in the specified namespace by their mere name; e.g.:
# Important:
# * Must be at the *start* of a file.
# * The System. part may NOT be omitted in this case.
using namespace System.Linq
[Enumerable] # Short for: [System.Linq.Enumerable]
To learn more about type literals in PowerShell, including how to specify generic types, see the bottom section of this answer.
Related
I am trying to use empty credentials for anonymous access. How to I set a variable to empty credentials. I am trying to use PSCredential.Empty Property
Santiago Squarzon has provided the solution:
[pscredential]::Empty
is the PowerShell equivalent of C#'s PSCredential.Empty, i.e. access to the static Empty property of the System.Management.Automation.PSCredential type.
PowerShell's syntax for accessing .NET types and their - instance or static - member:
In order to refer to a .NET type by its literal name in PowerShell, you must specify it as a type literal, which is the type name enclosed in [...]
[pscredential] works as-is, even in the absence of a using namespace System.Management.Automation statement, because it happens to be a type accelerator, i.e. a frequently used type that you can access by a short, unqualified name (which can, but needn't be the same name as that of the type it references; e.g., [xml] is short for System.Xml.XmlDocument).
Generally, you either need a using namespace statement if you want to use the unqualified name only, or you need to use the namespace-qualified type name; e.g., [System.Management.Automation.PSCredential] or - given that the System. component is optional - [Management.Automation.PSCredential]
For a complete discussion of PowerShell's type literals, see this answer.
While instance members of .NET types are accessed with . (member-access operator), just like in C#, accessing static members requires a different operator: ::, the static member-access operator.
A distinct operator is necessary, because type literals are objects themselves, namely instances of System.Reflection.TypeInfo, so that using . accesses the latter's instance properties; e.g. [pscredential].FullName returns the type's namespace-qualified name.
Also note that, in keeping with PowerShell's general case-insensitive nature, neither type literals nor member names need be specified case-exactly.
Hi everybody!
Short question:
Why does this work:
(Get-CimClass Win32_Share).CimClassMethods['Create'].Parameters
But this doesn’t:
(Get-CimClass Win32_Share).CimClassMethods.Create.Parameters
The same result can be achieved by:
((Get-CimClass Win32_Share).CimClassMethods|where-object {$_.name -match 'create'}).parameters
With the same logic I would expect that the same applies for:
(Get-command *help*)['get-help']
Or
(Get-command *help*)['cmdlet']
Or
(Get-childitem)['test.txt']
But it doesn’t. Only the method with where-object is possible here
Other considerations:
I know that this is should be the default way to retrive items from an hashtable and not pscustomobject but I also would like to better understand where else I can use this method. I searched the whole day on google but didn’t find anything.
Thanks in advance
Franco
The string-based indexing (['Create']) works, because the Microsoft.Management.Infrastructure.Internal.Data.CimMethodDeclarationCollection type that it is applied to implements a parameterized Item property that has a [string]-typed parameter (abstracted; verify with Get-Member Item -InputObject Get-CimClass Win32_Share).CimClassMethods):
# {get;} indicates that the property is read-only.
Item ParameterizedProperty <T> Item(string key) {get;}
Note: Strictly speaking, for something like ['Create'] to be accepted, it is sufficient for the Item property parameter to be [object]-typed, which is the case for the non-generic [hashtable] type, for instance.
For types with such a method, PowerShell, like C#, supports syntax ['Create'] as syntactic sugar for .Item('Create'), and the implication is that a key-based item lookup is performed.
Typically, such types also implement the IDictionary interface (e.g., [hasthable]), but that isn't the case here.
CimMethodDeclarationCollection behaves similarly to an [ordered] hashtable, in that it supports both positional (e.g, [0]) and key-based indexing (e.g., ['Create']),[1] but with one crucial difference:
PowerShell does not enumerate IDictionary-implementing types in the pipeline (you'll have to call .GetEnumerator() to achieve that), whereas CimMethodDeclarationCollection - due to not implementing IDictionary - is enumerated, like other collections such as arrays; that is, its elements / entries are sent one by one through the pipeline.
As for why something like the following doesn't work:
(Get-command help)['get-help']
Get-command *help* outputs multiple objects, which (...) automatically collects in a regular PowerShell array, of type [object[]].
[object[]] (based on System.Array), does not have the requisite parameterized property that would support ['get-help'] syntax - arrays support only positional indexing (e.g. [0] to refer to the first element), based on a parameterized Item property whose parameter is [int]-typed, implemented as part of the IList interface (abstracted; verify with Get-Member Item -InputObject #()):
# Note: IList.Item indicates that the property is implemented
# as part of the IList interface.
# {get;set;} indicates that the property is read-write.
Item ParameterizedProperty <T> IList.Item(int index) {get;set;}
However, given PowerShell's member-access enumeration feature it is reasonable to expect ['get-help'] to then be applied to the individual elements of the array, given that's how it works with ., the member-access operator (e.g., (Get-Command *help*).Name).
As of PowerShell 7.2.4, however, only ., not also ["someKey"] performs this enumeration; this surprising asymmetry is the subject of GitHub issue #17514.
[1] An [int]-typed key doesn't necessarily imply positional semantics, but does in the context of implementing the IList or ICollection interfaces (or their generic equivalent).
As the title suggests I am trying to reference a Nested Generic Type in Powershell Core.
I found that the + sign is used instead of the . for accessing a nested type in Powershell... but the syntax doesn't seem to work for "Nested Generic Types"... the compiler doesn't like the code an errors about the syntax.
Special use of plus (+) sign in Powershell
Has anyone been successful in getting this to work or is it a known limitation?
[System.Collections.Generic.Dictionary[[string], [int]]+Enumerator] GetEnumerator()
Daniel has provided the solution:
[System.Collections.Generic.Dictionary`2+Enumerator[string, int]]
returns the type of interest (note that the nested [...] around the individual generic type arguments, string and int, are optional).
That is, before being able to specify type arguments in order to construct a generic type, you must first specify its open form, which requires specifying the generic arity (the count of type arguments, <n>) in the form `<n> following the type name, namely `2 in the case of System.Collections.Generic.Dictionary<TKey,TValue> (expressed in C# notation), using the language-agnostic .NET API notation, as explained in this answer.
If no nested type (suffixed with +<typename>) is involved, specifying the arity is optional if all type arguments are specified; e.g., instead of [System.Collections.Generic.Dictionary`2[string, int]], [System.Collections.Generic.Dictionary[string, int]] is sufficient.
However, the enumerator type in question has no public constructor, so you cannot instantiate it directly; rather, it is the type returned when you call .GetEnumerator() on (a constructed form of) the enclosing type, [System.Collections.Generic.Dictionary`2]; verify with:
Get-Member -InputObject ([System.Collections.Generic.Dictionary[string, int]])::new().GetEnumerator()
I'm trying to invoke the List[T](IEnumerable) directly adding an item to the initial List like so, where T is a PowerShell class I've written (the below example uses the class name Thing:
$someObject = Get-Thing # returns a single object
$list = [List[Thing]]::new(#( $someObject ))
However, this yields an error suggesting it can't find the overload for this constructor:
Cannot find an overload for "List`1" and the argument count: "1".
Setting List[T] to the Object class works, however:
$someObject = Get-Thing
$list = [List[Object]]::new(#( $someObject ))
While this works, I'm unsure why I'm unable to use my PowerShell class as the type. My understanding is that only context-bound types and (by default) nested types are unable to be used with generics, but the following shows that my class is not a ContextBoundObject:
class Thing {
$Name
Thing($name) {
$this.Name = $name
}
}
$thing = [Thing]::new('Bender')
$thing -is [System.ContextBoundObject] # ==> False
I'm not certain if a PowerShell class would be a nested type of some sort, and about_Classes does not mention nested types.
I'm unsure why I'm unable to use my PowerShell class as the type
The array subexpression operator #() returns its results as [object[]] - a type which satisfies the argument type [IEnumerable[object]] - which is why it always works when you use [object] as the type parameter for the receiving collection type.
So, what to do about that?
If the array consists only of [Thing]'s, you can explicitly cast to a more specific collection type that implements [IEnumerable[Thing]]:
$list = [List[Thing]]::new([Thing[]]#( $someObject ))
To complement Mathias R. Jessen's helpful answer, which explains the problem well and offers an effective solution:
PowerShell's casts are not only syntactically more convenient, but also more flexible when it comes to on-demand type conversions.
Indeed, using a cast instead of calling a constructor, via the static ::new() method, does work:
using namespace System.Collections.Generic
class Thing { [string] $Name; Thing([string] $name) { $this.Name = $name } }
# Both of the following work:
# Single [Thing] instance.
$list = [List[Thing]] [Thing]::new('one')
# Multiple [Thing] instances, as an array, via the grouping operator, (...)
# #(...), the array subexpression operator, works too, but is unnecessary.
$list = [List[Thing]] ([Thing]::new('one'), [Thing]::new('two'))
PowerShell's automatic type conversions, as also used in casts:
Unfortunately, as of this writing the rules aren't documented, but a comment in the source-code provides a high-level overview, as does the (pretty low-level) ETS type converters documentation, which can be summarized as follows, in descending order of precedence:
First, engine-internal, fixed conversion rules may be applied (see source-code link above).
A notable internal rule concerns to-string conversions: while any .NET type supports it by an explicit call to its .ToString() method (inherited from the root of the object hierarchy, System.Object), PowerShell applies custom rules:
If a type has a culture-sensitive .ToString(<IFormatProvider>) overload, PowerShell passes the invariant culture deliberately, to achieve a culture-invariant representation, whereas a direct .ToString() call would yield a culture-sensitive representation - see this answer for details; e.g., in a culture where , is the decimal mark, [string] 1.2 returns '1.2' (period), whereas (1.2).ToString() returns '1,2' (comma).
Collections, including arrays, are stringified by concatenating their (stringified) elements with a space as the separator (by default, can be overridden with preference variable $OFS); e.g., [string] (1, 2) returns 1 2, whereas (1, 2).ToString() returns merely System.Object[].
Also, PowerShell converts freely:
between different number types (when possible).
between numbers and strings (in a culture-invariant manner, recognizing only . as the decimal mark when converting from a string).
and allows any data type to be converted to (interpreted as) as Boolean - see the bottom section of this answer for the rules.
Next, TypeConverter or (PSTypeConverter) classes that implement custom conversions for specific types are considered.
If the input type is a string ([string]), a static ::Parse() method is considered, if present: first, one with a culture-sensitive signature, ::Parse(<string>, <IFormatProvider>), in which case the invariant culture is passed, and, otherwise one with signature ::Parse(<string>).
Next, a single-argument constructor is considered, if the input type matches the argument's type or is convertible to it.
If an implicit or explicit conversion operator exists for conversion between the input and the target type.
Finally, if the input object implements the System.IConvertible interface and the target type is a supported-by-the-implementation primitive .NET type except [IntPtr] and [UIntPtr] or one of the following types: [datetime], [DBNull], [decimal].
Why is it that, in Powershell, the System.DayOfWeek enum can be referred to like [System.DayOfWeek], whereas the System.Environment.SpecialFolder enum must be referred to like [System.Environment+SpecialFolder] (note the plus character)?
My guess is because SpecialFolder is part of the static Environment class and DayOfWeek is sitting directly in the System namespace, but I'm having trouble finding any information on this. Normally static members would use the "static member operator", but that doesn't work in this case, nor does anything else I try except the mysterious plus character...
[System.DayOfWeek] # returns enum type
[enum]::GetValues([System.DayOfWeek]) # returns enum values
[enum]::GetValues([System.Environment.SpecialFolder]) # exception: unable to find type
[enum]::GetValues([System.Environment]::SpecialFolder) # exception: value cannot be null
[enum]::GetValues([System.Environment+SpecialFolder]) # returns enum values
System.Environment.SpecialFolder is definitely a type, and in C# both enums work the same way:
Enum.GetValues(typeof(System.Environment.SpecialFolder)) // works fine
Enum.GetValues(typeof(System.DayOfWeek)) // also works
I'd really like to understand why there's a distinction in Powershell and the reasoning behind this behaviour. Does anyone know why this is the case?
System.Environment.SpecialFolder is definitely a type
Type SpecialFolder, which is nested inside type Environment, is located in namespace System:
C# references that type as a full type name as in the quoted passage; that is, it uses . not only to separate the namespace from the containing type's name, but also to separate the latter from its nested type's name.
By contrast, PowerShell uses a .NET reflection method, Type.GetType(), to obtain a reference to the type at runtime:
That method uses a language-agnostic notation to identify types, as specified in documentation topic Specifying fully qualified type names.Tip of the hat to PetSerAl.
In that notation, it is + that is used to separate a nested type from its containing type (not ., as in C#).
That is, a PowerShell type literal ([...]) such as:
[System.Environment+SpecialFolder]
is effectively the same as taking the content between [ and ], System.Environment+SpecialFolder, and passing it as a string argument to Type.GetType, namely (expressed in PowerShell syntax):
[Type]::GetType('System.Environment+SpecialFolder')
Note that PowerShell offers convenient extensions (simplifications) to .NET's language-agnostic type notation, notably the ability to use PowerShell's type accelerators (such as [regex] for [System.Text.RegularExpressions.Regex]), the ability to omit the System. prefix from namespaces (e.g. [Collections.Generic.List`1[string]] instead of [System.Collections.Generic.List`1[string]]), and not having to specify the generic arity (e.g. `1) when a list of type argument is passed (e.g. [Collections.Generic.List[string]] instead of [Collections.Generic.List`1[string]] - see this answer) for more information.