【自分用】ValueTupleにSetValueする

# はじめに
この記事は見ないほうがいいです
もっといい記事見た方がいい、ChatGPTくんもそう言ってる

## やること
簡単なコンソールアプリ作ってる際にDB操作してて
いちいちクラス作ってやるの面倒だなと感じて
ValueTupleに対して、SetValueってできないかなと思い
Dapperでやれないかと見てたところサポートはしてないっぽいけど
出来てたので、じゃあどうやったらいいのか見た結果をメモ的に

## ではどうするか
大方のSetValueするやり方としては
```C#
object obj = new T();
var properties = typeof(T).GetProperties();
var hogeMemberInfo = properties.First(x => x.Name == "hoge");
hogeMemberInfo.SetValue(obj, "piyo"); 
```
といった感じで
Tクラスのインスタンス生成
そこからリフレクションによりPropertyInfoやMemberInfoを取得して
入れていく感じになると思われます

結局のところ、ValueTupleも同じ感じで

1. ValueTupleか確認する（これは必要であればでOK　「ValueTuple`（ValueTupleのItem個数）」となってるはず）
2. ValueTupleのインスタンス生成（ConstructorInfoを取得すればOK）
3. ValueTupleの中にある型（stringやらintやら）を見てPropertyInfo取る
4. PropertyInfoにSetValueする

気をつけたいのはC#でおなじみの方のタプルの記事（[https://ufcpp.net/study/csharp/datatype/tuples/](https://ufcpp.net/study/csharp/datatype/tuples/)）でも分かる通り
ValueTupleの内部実装は
**1個目から7個目まではItem1～7、8個目以降はRest.Item1～7となっていること**

:::note info
Rest以降に足りない場合は更にRestが続く
:::

要はRestがある場合はその分もConstructorInfoを取ってやる必要があるので
順番としては

1. ValueTupleのConstructorInfoを取得する
2. 1でRestがある場合はRest内のConstructorInfoを取得する 
3. 取得したConstructorInfo群から逆順でインスタンス生成を行う（Restの中身から作っていくため）
4. 生成したインスタンスに対してPropertyInfoを取得しSetValueを行う

と言った感じでこんな感じにテキトーにDapperのソースを参考に
```C#
// ValueTupleのConstructorInfoをRest含めて取得する
private (IEnumerable<ConstructorInfo>, IEnumerable<Type[]>) GetValueTupleConstructorsInfo(Type valueTupleType)
{
    var constructors = new List<ConstructorInfo>();
    var currentType = valueTupleType;
    var constructorFieldInfos = new List<Type[]>();
    while (true)
    {
        var restField = (FieldInfo)null;
        var valueTupleFields = currentType.GetFields(BindingFlags.Public | BindingFlags.GetField | BindingFlags.Instance | BindingFlags.DeclaredOnly);
        var arity = valueTupleFields.Any(x => x.Name == "Rest") ? valueTupleFields.Length - 1 : valueTupleFields.Length;
        var valueTupleFieldTypes = new Type[arity];

        for (int i = 0; i < valueTupleFields.Length; i++)
        {
            var info = valueTupleFields[i];
            if (info.Name == "Rest")
            {
                restField = info;
            }
            else if (info.Name.StartsWith("Item"))
            {
                var convertType = Nullable.GetUnderlyingType(info.FieldType) ?? info.FieldType;
                valueTupleFieldTypes[i] = convertType;
            }
        }

        var constructorArgs = restField != null ? valueTupleFieldTypes.Append(restField.FieldType).ToArray() : valueTupleFieldTypes;
        constructors.Add(currentType.GetConstructor(constructorArgs));
        constructorFieldInfos.Add(valueTupleFieldTypes);
        if (restField is null) break;
        currentType = restField.FieldType;
    }

    return (constructors, constructorFieldInfos);
}

// 
private void SetValueToValueTuple(IEnumerable<ConstructorInfo> constructors, IEnumerable<object[]> itemValueList, ref object obj)
{
    // Restにしているものから作成
    var value = (object)null;
    var restValue = (object)null;
    for (int i = constructors.Count() - 1; i >= 0; i--)
    {
        var constructor = constructors.ElementAt(i);
        var arguments = itemValueList.ElementAt(i);
        if (restValue != null)
        {
            arguments = arguments.Append(restValue).ToArray();
        }
        value = Convert.ChangeType(constructor.Invoke(arguments), constructor.DeclaringType);
        restValue = value;
    }

    obj = value;
}
```

## 結局のところ
需要はないと思う
というか、C#やってる人だったら誰でもわかっていることだと思うので
こんな記事書く必要もないんだろうなと

一応どっかのタイミングでGitHubには上げる予定

## 参考
1. [++C++ 未確認飛行 C](https://ufcpp.net)
2. [Dapper](https://github.com/DapperLib/Dapper)