Home > Net >  Tuple nuances in PowerShell
Tuple nuances in PowerShell

Time:07-27

1: Generic.List of tuples
I can create a [Generic.List] of Strings with [System.Collections.Generic.List[String]]::new(), but I can't get a [Generic.List] of Tuples with [System.Collections.Generic.List[System.Tuple]]::new(), I have to use [System.Collections.Generic.List[Object]]::new(). But what is weird is it doesn't fail here, it fails when I add. So this

$queue = [System.Collections.Generic.List[System.Tuple]]::new()
$tuple = [System.Tuple]::Create('One', 'Two', 'Three')
$queue.Add($tuple)
$queue

fails with Cannot find an overload for "Add" and the argument count: "1". at line 3. But a list of objects works fine. I would rather not use [Object] since that allows me to add anything to my list and I would rather be a bit more regorous with my typing. But more importantly I want to understand WHY [System.Collections.Generic.List[System.Tuple]] doesn't work.
EDIT: As seen below, if I type the members of the tuple, and then cast the values in the tuple Create() it works. But that still begs the question, why does an untyped tuple not work? I think this is basically academic, since I would prefer to type the values of the tuple as well as typing the members of the List. Rigor means complete rigor, not sloppy, half baked rigor.

2: Typed tuple
This blog shows the use of New-Object to create a Tuple with typed members using $tuple = New-Object “tuple[String,DateTime,Int]” “Joel Bennett”, “July 1, 2014”, 6. I wonder if there is a .NET constructor approach that I just can't find? This shows var tuple2 = new Tuple<string, double>("New York", 32.68); but I have yet to figure out how to do that in PowerShell.
EDIT: So it seems the issue here is that you can't depend on PS automatic typing inside .Create(), which makes sense. So, cast the values and it works.

$queue = [System.Collections.Generic.List[System.Tuple[String, DateTime, Int]]]::new()
$tuple = [System.Tuple]::Create([String]“Joel Bennett”, [DateTime]“July 1, 2014”, [Int]8)
$queue.Add($tuple)
$queue

3: Named Tuple members
This talks about the ability of VB to control the names of the Tuple members, since 'Item#' is both not very helpful and lame that these start at 1 while the Tuple is 0 indexed, which just annoys me. :) The question is, is this a VB only feature of Tuples? Or does a .NET tuple offer this functionality too, and in a way that is accessible from PS?

CodePudding user response:

Part 1 - Tuple vs Tuple<T1, T2, T3>

You're creating a list that can contain System.Tuples:

$queue = [System.Collections.Generic.List[System.Tuple]]::new()

$queue.GetType().FullName
# System.Collections.Generic.List`1[
#    [System.Tuple, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]
# ]

but then trying to store a generic Tuple<T1, T2, T3> in it:

$tuple = [System.Tuple]::Create('One', 'Two', 'Three')

$tuple.GetType().FullName
# System.Tuple`3[
#     [System.String, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],
#     [System.String, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],
#     [System.String, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]
# ]

System.Tuple is a static utility class for working with generic tuples, and isn’t interchangeable with specific instances of the generic classes (which themselves aren’t interchangeable with each other unless they have the exact same generic type parameters).

See https://docs.microsoft.com/en-us/dotnet/api/system.tuple?view=net-6.0#definition

Tuple Class

Provides static methods for creating tuple objects.

versus https://docs.microsoft.com/en-us/dotnet/api/system.tuple-3?view=net-6.0

Tuple<T1,T2,T3> Class

Represents a 3-tuple, or triple.

If you create a list of tuples with the desired generic type parameters you’ll be able to store your item in it:

$queue = [System.Collections.Generic.List[
    System.Tuple[string, string, string]
]]::new()

Part 2 - new Tuple<T1, T2, T3>

PowerShell will do some type inference for you on the generic type parameters, but we need to cast a datetime string to a proper [DateTime] if we want the second generic type parameter to be a [DateTime] instead of a string:

$tuple = [System.Tuple]::Create("Joel Bennett", [DateTime] "July 1, 2014", 8)

$tuple.GetType().FullName
# System.Tuple`3[
#     [System.String, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],
#     [System.DateTime, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],
#     [System.Int32, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]
# ]

If you specifically want to use the ::new syntax you can get a reference to the Tuple<T1, T2, T3> type with the desired generic type parameters and call ::new on that - PowerShell will then happily do its usual type coercion to try to pass the appropriate parameters. E.g:

$tuple = [System.Tuple[string, DateTime, int]]::new("Joel Bennett", "July 1, 2014", 8)

$tuple.GetType().FullName
# System.Tuple`3[
#     [System.String, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],
#     [System.DateTime, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],
#     [System.Int32, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]
# ]

Part 3 - named fields

Sorry - not sure there's an answer for this one.

I found a GitHub issue that discusses named properties briefly though, fwiw:

https://github.com/PowerShell/PowerShell/issues/10502#issue-491157520

  • Related