--- lang: ja-jp breaks: true --- # C# `ValueTupe` 構造体を動的に生成する方法 2021-10-31 ## かなりの力技になるが、以下の方法で動的に生成可能 ```csharp= public class DynamicValueTuple { static ExpressionMethodCache<DynamicValueTuple> s_methodCache; static DynamicValueTuple() { s_methodCache = new(); } // ========================================================================================= // private static ValueTuple<T1> Create<T1>() { return new ValueTuple<T1>(default(T1)); } private static ValueTuple<T1, T2> Create<T1, T2>() { return new ValueTuple<T1, T2>(default(T1), default(T2)); } private static ValueTuple<T1, T2, T3> Create<T1, T2, T3>() { return new ValueTuple<T1, T2, T3>(default(T1), default(T2), default(T3)); } private static ValueTuple<T1, T2, T3, T4> Create<T1, T2, T3, T4>() { return new ValueTuple<T1, T2, T3, T4>(default(T1), default(T2), default(T3), default(T4)); } private static ValueTuple<T1, T2, T3, T4, T5> Create<T1, T2, T3, T4, T5>() { return new ValueTuple<T1, T2, T3, T4, T5>(default(T1), default(T2), default(T3), default(T4), default(T5)); } private static ValueTuple<T1, T2, T3, T4, T5, T6> Create<T1, T2, T3, T4, T5, T6>() { return new ValueTuple<T1, T2, T3, T4, T5, T6>(default(T1), default(T2), default(T3), default(T4), default(T5), default(T6)); } private static ValueTuple<T1, T2, T3, T4, T5, T6, T7> Create<T1, T2, T3, T4, T5, T6, T7>() { return new ValueTuple<T1, T2, T3, T4, T5, T6, T7>(default(T1), default(T2), default(T3), default(T4), default(T5), default(T6), default(T7)); } private static ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8>> Create<T1, T2, T3, T4, T5, T6, T7, T8>() { return new ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8>>(default(T1), default(T2), default(T3), default(T4), default(T5), default(T6), default(T7), new ValueTuple<T8>(default(T8))); } private static ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9>> Create<T1, T2, T3, T4, T5, T6, T7, T8, T9>() { return new ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9>>(default(T1), default(T2), default(T3), default(T4), default(T5), default(T6), default(T7), new ValueTuple<T8, T9>(default(T8), default(T9))); } private static ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9, T10>> Create<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>() { return new ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9, T10>>(default(T1), default(T2), default(T3), default(T4), default(T5), default(T6), default(T7), new ValueTuple<T8, T9, T10>(default(T8), default(T9), default(T10))); } // ========================================================================================= // private static ValueTuple<T1> Create<T1>(T1 ti) { return new ValueTuple<T1>(ti); } private static ValueTuple<T1, T2> Create<T1, T2>(T1 t1, T2 t2) { return new ValueTuple<T1, T2>(t1, t2); } private static ValueTuple<T1, T2, T3> Create<T1, T2, T3>(T1 t1, T2 t2, T3 t3) { return new ValueTuple<T1, T2, T3> ( t1, t2, t3); } private static ValueTuple<T1, T2, T3, T4> Create<T1, T2, T3, T4>(T1 t1, T2 t2, T3 t3, T4 t4) { return new ValueTuple<T1, T2, T3, T4>(t1, t2, t3, t4); } private static ValueTuple<T1, T2, T3, T4, T5> Create<T1, T2, T3, T4, T5>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) { return new ValueTuple<T1, T2, T3, T4, T5>(t1, t2, t3, t4, t5); } private static ValueTuple<T1, T2, T3, T4, T5, T6> Create<T1, T2, T3, T4, T5, T6>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6) { return new ValueTuple<T1, T2, T3, T4, T5, T6>(t1, t2, t3, t4, t5, t6); } private static ValueTuple<T1, T2, T3, T4, T5, T6, T7> Create<T1, T2, T3, T4, T5, T6, T7>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7) { return new ValueTuple<T1, T2, T3, T4, T5, T6, T7>(t1, t2, t3, t4, t5, t6, t7); } private static ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8>> Create<T1, T2, T3, T4, T5, T6, T7, T8>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8) { return new ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8>>(t1, t2, t3, t4, t5, t6, t7, new ValueTuple<T8>(t8)); } private static ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9>> Create<T1, T2, T3, T4, T5, T6, T7, T8, T9>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9) { return new ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9>>(t1, t2, t3, t4, t5, t6, t7, new ValueTuple<T8, T9>(t8, t9)); } private static ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9, T10>> Create<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9, T10 t10) { return new ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9, T10>>(t1, t2, t3, t4, t5, t6, t7, new ValueTuple<T8, T9, T10>(t8, t9, t10)); } // ========================================================================================= // public static object Create(Type[] types) { int parameterCount = 0; if (types != null && types.Any()) { parameterCount = types.Length; } Type[] genericTypeArguments = types; Type[] parameters = null; string methodName = nameof(Create); Delegate func = s_methodCache.GetMethod_Static( genericTypeArguments, parameters, methodName ); return func.DynamicInvoke(); } public static object Create( Type[] types, object[] values ) { int parameterCount = 0; if (types != null && types.Any()) { parameterCount = types.Length; } Type[] genericTypeArguments = types; Type[] parameters = types; string methodName = nameof(Create); Delegate func = s_methodCache.GetMethod_Static( genericTypeArguments, parameters, methodName ); return func.DynamicInvoke(values); } public static ConstructorInfo GetConstructorInfo(Type[] types) { ConstructorInfo constructorInfo = null; if (types != null && types.Any()) { int typeCount = types.Length; if (typeCount > 8) { throw new Exception($"The maximum number of {nameof(ValueTuple)} parameters for which ConstructorInfo can be obtained is 8."); } object instance = DynamicValueTuple.Create(types); if (instance == null) { throw new Exception($"Failed to get the instance of {nameof(ValueTuple)}."); } constructorInfo = s_methodCache.GetConstructorInfo(instance.GetType(), types); if (constructorInfo == null) { throw new Exception($"Failed to get the constructor of {nameof(ValueTuple)}."); } } else { throw new Exception("The number of parameters of the ValueTuple that can obtain ConstructorInfo is one or more."); } return constructorInfo; } } ``` 以下、式木を使った共通的なキャッシュ機構。 ```csharp= public class ExpressionMethodCache<TClass> { private readonly Dictionary< Tuple< string /* class Type.FullName */, string /* methodName */, string /* parameter Type.FullName */ >, object /* Func */ > m_dicMethod; private object m_lokDic; public ExpressionMethodCache() : base() { m_dicMethod = new(); m_lokDic = new(); } public static ParameterExpression[] GetParameterExpressions(Type[] types) { if (types == null || types.Any() == false) { return null; } ParameterExpression[] expressions = new ParameterExpression[types.Length]; for (int i = 0; i < types.Length; i++) { expressions[i] = Expression.Parameter(types[i]); } return expressions; } public Delegate GetMethod_Static( Type[] genericTypeArguments, Type[] parameters, string methodName ) { string className = typeof(TClass).FullName; Type[] parametersType = new Type[0]; if (genericTypeArguments != null) { parametersType = parametersType.Concat(genericTypeArguments).ToArray(); } if (parameters != null) { parametersType = parametersType.Concat(parameters).ToArray(); } string fullname = String.Join("|", parametersType.Select(n => n.FullName)); var key = Tuple.Create(className, methodName, fullname); Delegate func; lock (m_lokDic) { object obj = null; if (m_dicMethod.TryGetValue(key, out obj) == false) { ParameterExpression[] parameterExpressions = GetParameterExpressions(parameters); MethodCallExpression methodCallExpression = Expression.Call( type : typeof(TClass), methodName : methodName, typeArguments : genericTypeArguments, arguments : parameterExpressions ); LambdaExpression lambda = Expression.Lambda( body : methodCallExpression, parameters : parameterExpressions ); func = lambda.Compile(); m_dicMethod.Add(key, func); } else { func = (Delegate)obj; } } return func; } public ConstructorInfo GetConstructorInfo( Type targetType, Type[] parameters ) { string className = targetType.FullName; string fullname = ""; if (parameters != null && parameters.Any()) { fullname = String.Join("|", parameters.Select(n => n.FullName)); } var key = Tuple.Create(className, nameof(GetConstructorInfo), fullname); ConstructorInfo constructorInfo; lock (m_lokDic) { object obj = null; if (m_dicMethod.TryGetValue(key, out obj) == false) { constructorInfo = targetType.GetConstructor(parameters); m_dicMethod.Add(key, constructorInfo); } else { constructorInfo = (ConstructorInfo)obj; } } return constructorInfo; } } ``` ###### tags: `C#` `ValueTupe` `リフレクション` `式木`