فهرست منبع

Added generictype builder;

Kenric Nugteren 4 ماه پیش
والد
کامیت
8daa2e3d99
1فایلهای تغییر یافته به همراه86 افزوده شده و 0 حذف شده
  1. 86 0
      InABox.Core/CoreUtils.cs

+ 86 - 0
InABox.Core/CoreUtils.cs

@@ -240,6 +240,92 @@ namespace InABox.Core
 
         public static bool HasAttribute<T>(this Type type) where T : Attribute => type.GetCustomAttribute<T>() != null;
 
+        public class GenericTypeBuilder
+        {
+            public Type Type { get; private set; }
+
+            public Type[] GenericTypeArguments { get; private set; }
+
+            private Type[] _genericArguments;
+
+            public GenericTypeBuilder(Type type)
+            {
+                Type = type;
+                _genericArguments = type.GetGenericArguments();
+                GenericTypeArguments = new Type[_genericArguments.Length];
+            }
+
+            /// <summary>
+            ///     Try to fill some type arguments based on the given <paramref name="arguments"/> to the <paramref name="interfaceType"/>.
+            /// </summary>
+            /// <remarks>
+            ///     <b>Example:</b>
+            ///     If <c>A&lt;T1, T2&gt;</c> implements <c>I1&lt;T1&gt;</c> and <c>I2&lt;T2&gt;</c>, then calling
+            ///     <c>AddInterface(typeof(I1&lt;&gt;), typeof(int))</c> will fill the <c>T1</c> slot - that is, we would produce
+            ///     <c>A&lt;int, T2&gt;</c>, and calling <c>AddInterface(typeof(I2&lt;&gt;), typeof(string))</c> will fill the <c>T2</c> slot with
+            ///     <c>string</c>, such that if both are called, we will obtain <c>A&lt;int, string&gt;</c>.
+            /// </remarks>
+            public GenericTypeBuilder AddInterface(Type interfaceType, params Type[] arguments)
+            {
+                Debug.Assert(arguments.Length == interfaceType.GetGenericArguments().Length, "Wrong number of arguments passed.");
+                if(Type.GetInterfaceDefinition(interfaceType) is Type inter)
+                {
+                    for(int i = 0; i < arguments.Length; ++i)
+                    {
+                        var idx = Array.IndexOf(_genericArguments, inter.GenericTypeArguments[i]);
+                        GenericTypeArguments[idx] = arguments[i];
+                    }
+                }
+                return this;
+            }
+
+            /// <summary>
+            /// Like <see cref="AddInterface(Type, Type[])"/>, but obtaining the arguments from <paramref name="otherType"/>'s implementation
+            /// of <paramref name="interfaceType"/>.
+            /// </summary>
+            public GenericTypeBuilder AddInterfaceFrom(Type interfaceType, Type otherType)
+            {
+                if(otherType.GetInterfaceDefinition(interfaceType) is Type inter)
+                {
+                    AddInterface(interfaceType, inter.GenericTypeArguments);
+                }
+                return this;
+            }
+
+            /// <summary>
+            /// Build the generic type. If not all the type arguments are filled, this returns <see langword="null"/>.
+            /// </summary>
+            /// <returns>
+            /// The completed type, or <see langword="null"/> if the method fails.
+            /// </returns>
+            public Type? Build()
+            {
+                if(GenericTypeArguments.Any(x => x is null))
+                {
+                    return null;
+                }
+
+                return Type.MakeGenericType(GenericTypeArguments);
+            }
+
+            /// <summary>
+            /// See <see cref="Build"/>.
+            /// </summary>
+            public bool TryBuild([NotNullWhen(true)] out Type? result)
+            {
+                result = Build();
+                return result != null;
+            }
+        }
+
+        /// <summary>
+        /// Attempt to build this generic type into a full type, using the <see cref="GenericTypeBuilder"/> class,
+        /// allowing to fill generic type arguments based on interfaces.
+        /// </summary>
+        public static GenericTypeBuilder BuildGenericType(this Type type)
+        {
+            return new GenericTypeBuilder(type);
+        }
 
         #endregion