LoginSignup
0
1

More than 1 year has passed since last update.

【自分用】ValueTupleにSetValueする

Posted at

はじめに

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

やること

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

ではどうするか

大方のSetValueするやり方としては

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/)でも分かる通り
ValueTupleの内部実装は
1個目から7個目まではItem1~7、8個目以降はRest.Item1~7となっていること

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

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

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

と言った感じでこんな感じにテキトーにDapperのソースを参考に

// 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
  2. Dapper
0
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1