I have a use case where I need to check if a value is a C# 7 ValueTuple, and if so, loop through each of the items. I tried checking with obj is ValueTuple
and obj is (object, object)
but both of those return false. I found that I could use obj.GetType().Name
and check if it starts with "ValueTuple"
but that seems lame to me. Any alternatives would be welcomed.
I also have the issue of getting each item. I attempted to get Item1
with the solution found here: How do I check if a property exists on a dynamic anonymous type in c#? but ((dynamic)obj).GetType().GetProperty("Item1")
returns null. My hope was that I could then do a while
to get each item. But this does not work. How can I get each item?
Update - more code
if (item is ValueTuple) //this does not work, but I can do a GetType and check the name
{
object tupleValue;
int nth = 1;
while ((tupleValue = ((dynamic)item).GetType().GetProperty($"Item{nth}")) != null && //this does not work
nth <= 8)
{
nth++;
//Do stuff
}
}
So, C# provides another class that is Tuple class which contains the static methods for creating tuple object without providing the type of each element.
Structures do not inherit in C#, so ValueTuple<T1>
, ValueTuple<T1,T2>
, ValueTuple<T1,T2,T3>
and so on are distinct types that do not inherit from ValueTuple
as their base. Hence, obj is ValueTuple
check fails.
If you are looking for ValueTuple
with arbitrary type arguments, you can check if the class is ValueTuple<,...,>
as follows:
private static readonly Set<Type> ValTupleTypes = new HashSet<Type>(
new Type[] { typeof(ValueTuple<>), typeof(ValueTuple<,>),
typeof(ValueTuple<,,>), typeof(ValueTuple<,,,>),
typeof(ValueTuple<,,,,>), typeof(ValueTuple<,,,,,>),
typeof(ValueTuple<,,,,,,>), typeof(ValueTuple<,,,,,,,>)
}
);
static bool IsValueTuple2(object obj) {
var type = obj.GetType();
return type.IsGenericType
&& ValTupleTypes.Contains(type.GetGenericTypeDefinition());
}
To get sub-items based on the type you could use an approach that is not particularly fast, but should do the trick:
static readonly IDictionary<Type,Func<object,object[]>> GetItems = new Dictionary<Type,Func<object,object[]>> {
[typeof(ValueTuple<>)] = o => new object[] {((dynamic)o).Item1}
, [typeof(ValueTuple<,>)] = o => new object[] {((dynamic)o).Item1, ((dynamic)o).Item2}
, [typeof(ValueTuple<,,>)] = o => new object[] {((dynamic)o).Item1, ((dynamic)o).Item2, ((dynamic)o).Item3}
, ...
};
This would let you do this:
object[] items = null;
var type = obj.GetType();
if (type.IsGeneric && GetItems.TryGetValue(type.GetGenericTypeDefinition(), out var itemGetter)) {
items = itemGetter(obj);
}
Regarding the part of the question "How can I get each item?"...
Both ValueTuple and Tuple both implement ITuple
, which has a length property and an indexer property. So a the following console app code lists the values to the console:
// SUT (as a local function)
IEnumerable<object> GetValuesFromTuple(System.Runtime.CompilerServices.ITuple tuple)
{
for (var i = 0; i < tuple.Length; i++)
yield return tuple[i];
}
// arrange
var valueTuple = (StringProp: "abc", IntProp: 123, BoolProp: false, GuidProp: Guid.Empty);
// act
var values = GetValuesFromTuple(valueTuple);
// assert (to console)
Console.WriteLine($"Values = '{values.Count()}'");
foreach (var value in values)
{
Console.WriteLine($"Value = '{value}'");
}
Console output:
Values = '4'
Value = 'abc'
Value = '123'
Value = 'False'
Value = '00000000-0000-0000-0000-000000000000'
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With