using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Reflection; using System.Text; namespace gaemstone.Utility; public static class ReflectionExtensions { public static string GetFriendlyName(this Type type) { if (!type.IsGenericType) return type.Name; var name = type.Name; var sb = new StringBuilder(name[..name.IndexOf('`')]); sb.Append('<'); sb.AppendJoin(",", type.GenericTypeArguments.Select(GetFriendlyName)); sb.Append('>'); return sb.ToString(); } public static T? Get(this MemberInfo member) where T : Attribute => member.GetCustomAttribute(); public static IEnumerable GetMultiple(this MemberInfo member) where T : Attribute => member.GetCustomAttributes(); public static bool Has(this MemberInfo member) where T : Attribute => member.GetCustomAttribute() != null; public static T? Get(this ParameterInfo member) where T : Attribute => member.GetCustomAttribute(); public static IEnumerable GetMultiple(this ParameterInfo member) where T : Attribute => member.GetCustomAttributes(); public static bool Has(this ParameterInfo member) where T : Attribute => member.GetCustomAttribute() != null; public static bool IsNullable(this PropertyInfo property) => IsNullable(property.PropertyType, property.DeclaringType, property.CustomAttributes); public static bool IsNullable(this FieldInfo field) => IsNullable(field.FieldType, field.DeclaringType, field.CustomAttributes); public static bool IsNullable(this ParameterInfo parameter) => IsNullable(parameter.ParameterType, parameter.Member, parameter.CustomAttributes); // https://stackoverflow.com/a/58454489 static bool IsNullable(Type memberType, MemberInfo? declaringType, IEnumerable customAttributes) { if (memberType.IsValueType) return (Nullable.GetUnderlyingType(memberType) != null); var nullable = customAttributes.FirstOrDefault( x => (x.AttributeType.FullName == "System.Runtime.CompilerServices.NullableAttribute")); if ((nullable != null) && (nullable.ConstructorArguments.Count == 1)) { var attributeArgument = nullable.ConstructorArguments[0]; if (attributeArgument.ArgumentType == typeof(byte[])) { var args = (ReadOnlyCollection)attributeArgument.Value!; if ((args.Count > 0) && (args[0].ArgumentType == typeof(byte))) return (byte)args[0].Value! == 2; } else if (attributeArgument.ArgumentType == typeof(byte)) return (byte)attributeArgument.Value! == 2; } for (var type = declaringType; type != null; type = type.DeclaringType) { var context = type.CustomAttributes.FirstOrDefault( x => (x.AttributeType.FullName == "System.Runtime.CompilerServices.NullableContextAttribute")); if ((context != null) && (context.ConstructorArguments.Count == 1) && (context.ConstructorArguments[0].ArgumentType == typeof(byte))) return (byte)context.ConstructorArguments[0].Value! == 2; } // Couldn't find a suitable attribute. return false; } }