How to iterate over items in a Tuple, when I dont know at compile-time what are the types the tuple is composed of? I just need an IEnumerable of 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;
}
}
You can access properties and their values by reflection with Type.GetProperties
var values = tuple.GetType().GetProperties().Select(p => p.GetValue(tuple));
So 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));
}
An issue here is that you have to deal with multiple Tuple
types: Tuple<T1, T2>
, Tuple<T1, T2, T3>
etc. (I'm assuming that you want this to work with tuples with an arbitrary number of items.)
A somewhat hacky way of doing this it to see if the name of the type 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 the hackyness of FullName.StartsWith(...)
you can make it more typesafe like so:
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;
}
}
your code does not work as expected because you are expecting an exact match of Tuple<object,object>
when you use as Tuple which is not the case
You can try the following to be more generic (if you expect always a 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;
}
}
}
In .NET Core 2.0+ or .NET Framework 4.7.1+, there are
it's from an interface ITuple interface
var data = (123, "abc", 0.983, DateTime.Now);
ITuple iT = data as ITuple;
for(int i=0; i<iT.Length;i++)
Console.WriteLine(iT[i]);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.