4

If I use the [char] datatype, it requires the value to be one character, e.x.

[char]"a"
a

Since if I use it with more than one character it will give me an error:

[char]"ab"
Cannot convert value "ab" to type "System.Char". Error: "String must be exactly one
character long."
At line:1 char:1
+ [char]"ab"
+ ~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvalidCastParseTargetInvocation

However if I use

[char[]]"ab"

I get the output

a
b

If I compare Get-Member on both, I get no result:

PS C:\Users\nijoh> Compare-Object -ReferenceObject $([char] | gm) -DifferenceObject $([char[]] | gm) -PassThru
PS C:\Users\nijoh>

But I can see that they are two distinct types since they show up differently:

PS C:\Users\nijoh> ([char[]]"a").GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Char[]                                   System.Array


PS C:\Users\nijoh> ([char]"a").GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Char                                     System.ValueType

So what is the difference between the [char] and [char[]] datatypes in Powershell?

Nikola Johnson
  • 479
  • 1
  • 4
  • 11

1 Answers1

6

In PowerShell, a type name enclosed in [...] is a type literal, i.e. an object that represents the .NET type named.

Independently of that, inside [...], [] immediately after a type name represents an array containing elements of that type; for an overview of the notation used inside [...], see this answer.

Therefore:

  • [char] refers to type System.Char (the System. prefix is generally optional in PowerShell; additionally - as is the case here - PowerShell has a fixed set of type accelerators that allow you to use simple names for types located in any namespace - see this answer).

    • To get a given type literal's full (namespace-qualified) .NET type name, use [...].FullName; e.g., [char].FullName yields System.Char
  • [char[]] refers to an array type whose elements are of type char ( System.Char); [char[]].FullName yields System.Char[].


Since if I use it with more than one character it will give me an error ([char]"ab")

PowerShell has no [char] literals, only string ([string]) literals. When you cast a string to [char] - which represents a single character - only a single-character string is accepted; e.g. [char] 'a' works, but [char] 'ab' or [char] "ab" doesn't.

However if I use [char[]]"ab" ...

Casting a string to an array of characters returns a character array comprised of the string's individual characters.

In other words: [char[]] "ab" is equivalent to "ab".ToCharArray().

If I compare Get-Member on both, I get no result:

The reason is that Get-Member operates on the types of the input objects, and if the input objects are type literals - such as [char] and [char[]] - their type is examined, which is System.RuntimeType, a non-public PowerShell type that derives from System.Reflection.TypeInfo, which describes a .NET type.

In other words: all type literals piped to Get-Member will result in the same output, irrespective of what specific type they refer to, because it is the one and only type-describing type whose members are being reported.

Therefore, using Compare-Object on Get-Member calls with different type literals predictably produces no output, because the Get-Member calls result in the same output[1]. (Not producing output is Compare-Object's way of indicating that no differences were detected.)


[1] Get-Member outputs an array of Microsoft.PowerShell.Commands.MemberDefinition instances, one for each member of the input object's type. In the absence of a -Property argument passed to Compare-Object, these instances are compared as a whole, by their .ToString() value, which yields a meaningful representation of each member.

mklement0
  • 382,024
  • 64
  • 607
  • 775