0

I was looking for an alternative to a local structure in C#, which is possible in C/C++ but not possible in C#. That question introduced me to the lightweight System.ValueTuple type, which appeared in C# 7.0 (.NET 4.7).

Say I have two tuples defined in different ways:

var book1 = ("Moby Dick", 123);
(string title, int pages) book2 = ("The Art of War", 456);

Both tuples contain two elements. The type of Item1 is System.String, and the type of Item2 is System.Int32 in both tuples.

How do you determine the number of elements in a tuple variable? And can you iterate over those elements using something like foreach?

A quick reading of the official documentation on System.ValueTuple doesn't appear to have the information.

AlainD
  • 5,413
  • 6
  • 45
  • 99
  • you can do it with reflection - `.GetType()` is a great starting point – Daniel A. White Feb 22 '22 at 15:21
  • 2
    If you need arbitrary enumeration, a `ValueTuple` is arguably not what you want. The whole "lightweight" thing rather flies out the window if you need to reflect over it. Indeed, there really is no "lightweight" way of handling a type (any type) where you don't know the structure in advance (assuming we don't consider `object[]` "lightweight") -- and if you do there's no need for enumerating. – Jeroen Mostert Feb 22 '22 at 15:24
  • 2
    Why would you do that? Presumably you'd know what the type is when writing the code and thus how many items are in the tuple. Note you can use deconstruction to get the values like `var (name, pages) = book1;` rather than trying to iterate the items. – juharr Feb 22 '22 at 15:24
  • @juharr: True, I'm not yet sure how useful this would be, but being able to iterate over the items means that a generic method could be developed. Determining the number of items is possible by processing the value of `myTuple.GetType().ToString()`...but this just seems weird, surely there's a better way? – AlainD Feb 22 '22 at 15:38
  • `System.ValueTuple` is actually 9 different `struct`s (including one without type parameters). The number of items is the number of generic type parameters on the value tuple type you're using, so `book1` (or `book2`) `.GetType().GetGenericTypeDefinition.GetTypeParameters().Length` will return `2`. If you get `8`, then you need to test if the last one also a tuple and recursively count those items. – madreflection Feb 22 '22 at 15:41
  • 1
    That said, all 9 of them also implement `ITuple`, which has a `Length` property. The one that has the final `TRest` type parameter will also do the recursion so you don't have to. But unless you're accepting the tuple in boxed form, there's really no need to get the length because, as juharr pointed out, you'll already know what the length is because of how you wrote the code. – madreflection Feb 22 '22 at 15:45
  • 1
    You *could* just write methods for all of them -- `IEnumerable Items(ValueTuple v) { yield return v.Item1; yield return v.Item2; }` and so on and so forth, but now you're 1) boxing every single tuple element and 2) losing type info for that element, so it's actually worse than `Dictionary` -- which at least preserves names. If you needed this kind of "generic access" at all, compile-time code generation would make more sense (repeating a piece of code for every member of the tuple). – Jeroen Mostert Feb 22 '22 at 15:49

2 Answers2

5

Yes you can iterate through the Items via the following for loop:

var tuple = ("First", 2, 3.ToString());
ITuple indexableTuple = (ITuple)tuple;
for (var itemIdx = 0; itemIdx < indexableTuple.Length; itemIdx++)
{
    Console.WriteLine(indexableTuple[itemIdx]);
}
  • The trick is that you need to explicitly convert your ValueTuple to ITuple
  • That interface exposes Length and index operator

ITuple resides inside the System.Runtime.CompilerServices namespace.

Peter Csala
  • 17,736
  • 16
  • 35
  • 75
1

I guess what you're looking for is something like this:

    public void Run(string[] args)
    {
        Tuple<string, string, int> myTuple = new Tuple<string, string, int>("1st", "2nd", 3);
        LoopThroughTupleInstances(myTuple);
    }

    private static void LoopThroughTupleInstances(System.Runtime.CompilerServices.ITuple tuple)
    {
        for (int i = 0; i < tuple.Length; i++)
        {
            Console.WriteLine($"Type: {tuple[i].GetType()}, Value: {tuple[i]}");
        }
    }
Nic71
  • 21
  • 5
  • What about if you had `(string title, int pages) book3 = (null, 0);` ? – madreflection Feb 22 '22 at 16:04
  • The question was how do you determine the number of elements in a tuple variable and iterate over those elements. Obv this clearly needs null handling but hopefully shows how to determine the length and iterate over the elements :) – Nic71 Feb 22 '22 at 16:18
  • If you're going to argue the scope of the question, then `tuple.Length` was all you needed. But you've exceeded that scope in a way that can throw an exception. – madreflection Feb 22 '22 at 16:27