How to iterate over the elements of a tuple

How can I iterate over the elements in a tuple when I don't know at compile time what are the types that the tuple consists of? I just need IEnumerable objects (for serialization).

private static IEnumerable TupleToEnumerable(object tuple)
{
    Type t = tuple.GetType();
    if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Tuple<,>))
    {
        var x = tuple as Tuple<object, object>;
        yield return x.Item1;
        yield return x.Item2;
    }
}

      

+3


source to share


3 answers


You can access properties and their values ​​by reflection with Type.GetProperties

var values = tuple.GetType().GetProperties().Select(property => property.GetValue(tuple));

      



This way your method will be very simple. Linq query

private static IEnumerable TupleToEnumerable(object tuple)
{
    // You can check if type of tuple is actually Tuple
    return tuple.GetType()
                .GetProperties()
                .Select(property => property.GetValue(tuple));
}

      

+2


source


The problem is that you have to deal with several types Tuple

: Tuple<T1, T2>

, Tuple<T1, T2, T3>

etc. (I'm assuming you want this to work for tuples with an arbitrary number of elements.)

A somewhat hacky way to do this is to find out if a type name begins with System.Tuple

:

public static IEnumerable TupleToEnumerable(object tuple)
{
    Type t = tuple.GetType();

    if (t.IsGenericType && t.GetGenericTypeDefinition().FullName.StartsWith("System.Tuple"))
    {
        for (int i = 1;; ++i)
        {
            var prop = t.GetProperty("Item" + i);

            if (prop == null)
                yield break;

            yield return prop.GetValue(tuple);
        }
    }
}

      



If you don't like hacking FullName.StartsWith(...)

, you can make it more typical:

public static IEnumerable TupleToEnumerable(object tuple)
{
    Type t = tuple.GetType();

    if (isTupleType(t))
    {
        for (int i = 1;; ++i)
        {
            var prop = t.GetProperty("Item" + i);

            if (prop == null)
                yield break;

            yield return prop.GetValue(tuple);
        }
    }
}

private static bool isTupleType(Type type)
{
    if (!type.IsGenericType)
        return false;

    var def = type.GetGenericTypeDefinition();

    for (int i = 2;; ++i)
    {
        var tupleType = Type.GetType("System.Tuple`" + i);

        if (tupleType == null)
            return false;

        if (def == tupleType)
            return true;
    }
}

      

+3


source


your code doesn't work as expected because you expect an exact match Tuple<object,object>

when used as a Tuple, which is not the case.

You can try the following more general one (if you always expect two items)

 class Program
    {
        static void Main(string[] args)
        {
            Tuple<string, string> tuples = new Tuple<string, string>("test","test");
            foreach (string item in TupleToEnumerable<string>(tuples))
            {
                Console.WriteLine(item);   

            }
        }

        private static IEnumerable<T> TupleToEnumerable<T>(object tuple)
        {
            Type t = tuple.GetType();
            if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Tuple<,>))
            {
                var x = tuple as Tuple<T, T>;
                yield return x.Item1;
                yield return x.Item2;
            }
        }
    }

      

0


source







All Articles