1

In my C# application, I have the need to create an instance of a Tuple. But here's the catch - I don't know the Tuple type in advance, only at runtime. If I have methods that can create instances of any types likely to be in the Tuple, then is there a way to use reflection to create such a Tuple instance? For example, I'm thinking along these lines, but I can't get it to work:

if (type typeof(Tuple))
{
    FieldInfo[] fieldInfoArray = type.GetFields();
    object[] = CreateArrayOfObjectsFromFieldInfoArray(fieldInfoArray);
    return new Tuple(objectArray);
}

Is there a better way to do this?

user304582
  • 1,910
  • 5
  • 28
  • 49
  • 2
    Is this what you´re looking for: https://stackoverflow.com/questions/266115/pass-an-instantiated-system-type-as-a-type-parameter-for-a-generic-class – MakePeaceGreatAgain Nov 21 '17 at 19:17
  • how about Tuple – Steve Nov 21 '17 at 19:20
  • I guess u can also use Tuple.Create like Tuple.Create("cat", 2, true); – Daniel B Nov 21 '17 at 19:22
  • Wow - three comments already! I don't know the length or types or the Tuple members, so can't use suggestions 2 or 3. I read through the referenced question, but attempting to use something like defaultObject = Activator.CreateInstance(type, true); throws an exception No parameterless constructor defined for this object. – user304582 Nov 21 '17 at 19:27

2 Answers2

1

You can use the Tuple.Create method to much more easily construct the object than using a constructor (both when writing the code statically, and also using reflection). You can use the FieldInfo objects to figure out which overload of Create to call, and then pass in the values that you have when invoking that overload.

FieldInfo[] fieldInfoArray = type.GetFields();
object[] values = CreateArrayOfObjectsFromFieldInfoArray(fieldInfoArray);
Type[] typeArguments = fieldInfoArray.Select(fieldInfo => fieldInfo.FieldType).ToArray();
return typeof(Tuple).GetMethods()
    .FirstOrDefault(method => method.Name == "Create" && method.GetParameters().Length == typeArguments.Length)
    ?.MakeGenericMethod(typeArguments)
    ?.Invoke(null, values);
Servy
  • 202,030
  • 26
  • 332
  • 449
  • This looks good -in order to try it, I need to test if a type is a Tuple. I thought the following would work, but it does not: typeof(Tuple).IsAssignableFrom(type); What would be the right way to test for the Tuple type? – user304582 Nov 21 '17 at 20:55
  • @user304582 If the type is *already* a `Tuple` you don't need to create a new one, you already have one. Regardless, that's an entirely different question from the one that you asked. – Servy Nov 21 '17 at 20:58
  • Great, thanks - I'll do some googling on the test for a Tuple, and post a separate question if need be. In the meantime, I'll mark your answer as accepted. – user304582 Nov 21 '17 at 21:08
  • This code seems to work to check for a Tuple type: Type genericType = type.GetGenericTypeDefinition(); if (genericType.Equals(typeof(Tuple<>)) || genericType.Equals(typeof(Tuple<,>)) || genericType.Equals(typeof(Tuple<,,>)) || genericType.Equals(typeof(Tuple<,,,>)) || genericType.Equals(typeof(Tuple<,,,,>)) || genericType.Equals(typeof(Tuple<,,,,,>)) || genericType.Equals(typeof(Tuple<,,,,,,>)) || genericType.Equals(typeof(Tuple<,,,,,,,>))) { return true; } – user304582 Nov 21 '17 at 22:17
  • Unfortunately, I hit a problem with the call: type.GetFields(). Instead of returning an expected FieldInfo array of length 2, I get an array of length 0 Here is the type definition for the Tuple: type = {Name = "Tuple`2" FullName = "System.Tuple`2[[System.String, ...],[System.Byte[], ...]]"} I tried various BindingFlags combinations without success as well. Any ideas? – user304582 Nov 22 '17 at 00:30
  • I solved the problem of getting the types using this call: Type[] typeArray = type.GetGenericArguments(); This returns an array of length two, first a String and the second a byte array as expected. – user304582 Nov 22 '17 at 00:47
0

I could not get the code in the first answer to work, but after some trial and error, I got the following to work:

Type[] typeArray = type.GetGenericArguments();
object[] values = CreateArrayOfObjectsFromFieldInfoArray(typeArray);
defaultObject = type.GetConstructor(typeArray)?.Invoke(values);
user304582
  • 1,910
  • 5
  • 28
  • 49