|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Reflection;
namespace System.ComponentModel.Composition.ReflectionModel
{
internal static class GenericServices
{
internal static IList<Type> GetPureGenericParameters(this Type type)
{
ArgumentNullException.ThrowIfNull(type);
if (type.IsGenericType && type.ContainsGenericParameters)
{
List<Type> pureGenericParameters = new List<Type>();
TraverseGenericType(type, (Type t) =>
{
if (t.IsGenericParameter)
{
pureGenericParameters.Add(t);
}
});
return pureGenericParameters;
}
else
{
return Type.EmptyTypes;
}
}
internal static int GetPureGenericArity(this Type type)
{
ArgumentNullException.ThrowIfNull(type);
int genericArity = 0;
if (type.IsGenericType && type.ContainsGenericParameters)
{
TraverseGenericType(type, (Type t) =>
{
if (t.IsGenericParameter)
{
genericArity++;
}
});
}
return genericArity;
}
private static void TraverseGenericType(Type type, Action<Type> onType)
{
if (type.IsGenericType)
{
foreach (Type genericArgument in type.GetGenericArguments())
{
TraverseGenericType(genericArgument, onType);
}
}
onType(type);
}
public static int[] GetGenericParametersOrder(Type type)
{
return type.GetPureGenericParameters().Select(parameter => parameter.GenericParameterPosition).ToArray();
}
public static string GetGenericName(string originalGenericName, int[] genericParametersOrder, int genericArity)
{
string[] genericFormatArgs = new string[genericArity];
for (int i = 0; i < genericParametersOrder.Length; i++)
{
genericFormatArgs[genericParametersOrder[i]] = $"{{{i}}}";
}
return string.Format(CultureInfo.InvariantCulture, originalGenericName, genericFormatArgs);
}
public static T[] Reorder<T>(T[] original, int[] genericParametersOrder)
{
T[] genericSpecialization = new T[genericParametersOrder.Length];
for (int i = 0; i < genericParametersOrder.Length; i++)
{
genericSpecialization[i] = original[genericParametersOrder[i]];
}
return genericSpecialization;
}
[return: NotNullIfNotNull(nameof(types))]
public static IEnumerable<Type>? CreateTypeSpecializations(this Type[]? types, Type[] specializationTypes)
{
if (types == null)
{
return null;
}
else
{
return types.Select(type => type.CreateTypeSpecialization(specializationTypes));
}
}
public static Type CreateTypeSpecialization(this Type type, Type[] specializationTypes)
{
if (!type.ContainsGenericParameters)
{
return type;
}
if (type.IsGenericParameter)
{
// the only case when MakeGenericType won't work is when the 'type' represents a "naked" generic type
// in this case we simply grab the type with the proper index from the specializtion
return specializationTypes[type.GenericParameterPosition];
}
else
{
Type[] typeGenericArguments = type.GetGenericArguments();
Type[] subSpecialization = new Type[typeGenericArguments.Length];
for (int i = 0; i < typeGenericArguments.Length; i++)
{
Type typeGenericArgument = typeGenericArguments[i];
subSpecialization[i] = typeGenericArgument.IsGenericParameter ?
specializationTypes[typeGenericArgument.GenericParameterPosition] : typeGenericArgument;
}
// and "close" the generic
return type.GetGenericTypeDefinition().MakeGenericType(subSpecialization);
}
}
public static bool CanSpecialize(Type? type, IEnumerable<Type>? constraints, GenericParameterAttributes attributes)
{
return CanSpecialize(type, constraints) && CanSpecialize(type!, attributes);
}
public static bool CanSpecialize(Type? type, IEnumerable<Type>? constraintTypes)
{
if (constraintTypes == null)
{
return true;
}
// where T : IFoo
// a part of where T : struct is also handled here as T : ValueType
foreach (Type constraintType in constraintTypes)
{
if ((constraintType != null) && !constraintType.IsAssignableFrom(type))
{
return false;
}
}
return true;
}
public static bool CanSpecialize(Type type, GenericParameterAttributes attributes)
{
if (attributes == GenericParameterAttributes.None)
{
return true;
}
// where T : class
if ((attributes & GenericParameterAttributes.ReferenceTypeConstraint) != 0)
{
if (type.IsValueType)
{
return false;
}
}
// where T : new
if ((attributes & GenericParameterAttributes.DefaultConstructorConstraint) != 0)
{
// value types always have default constructors
if (!type.IsValueType && ((type.GetConstructor(Type.EmptyTypes) == null) || type.IsAbstract))
{
return false;
}
}
// where T : struct
if ((attributes & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0)
{
// must be a value type
if (!type.IsValueType)
{
return false;
}
// Make sure that the type is not nullable
// this is salways guaranteed in C#, but other languages may be different
if (Nullable.GetUnderlyingType(type) != null)
{
return false;
}
}
// all other fals indicate variance and don't place any actual restrictions on the generic parameters
// but rather how they should be used by the compiler
return true;
}
}
}
|