Files
RobustToolbox/Robust.Shared/Utility/TypeHelpers.cs
Leon Friedrich 9af119f57a Toolshed Rejig (#5455)
* Toolshed Rejig

* shorten hint string

* Try fix conflicts. Ill make with work later

* bodge

* Fix ProtoIdTypeParser assert

* comment

* AllEntities

* Remove more linq from WhereCommand

* better help strings

* Add ContainsCommand

* loc strings

* Add contains command description

* Add $self variable

* Errors for writing to readonly variables

* A
2024-12-21 17:49:11 +11:00

224 lines
7.1 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace Robust.Shared.Utility
{
public static class TypeHelpers
{
/// <summary>
/// Returns absolutely all fields, privates, readonlies, and ones from parents.
/// </summary>
public static IEnumerable<FieldInfo> GetAllFields(this Type t)
{
// We need to fetch the entire class hierarchy and SelectMany(),
// Because BindingFlags.FlattenHierarchy doesn't read privates,
// Even when you pass BindingFlags.NonPublic.
foreach (var p in GetClassHierarchy(t))
{
foreach (var field in p.GetFields(
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.DeclaredOnly |
BindingFlags.Public))
{
yield return field;
}
}
}
/// <summary>
/// Returns absolutely all instance properties on a type. Inherited and private included.
/// </summary>
public static IEnumerable<PropertyInfo> GetAllProperties(this Type t)
{
return GetClassHierarchy(t).SelectMany(p =>
p.GetProperties(
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.DeclaredOnly |
BindingFlags.Public));
}
/// <summary>
/// Checks whether a property is the "base" definition.
/// So basically it returns false for overrides.
/// </summary>
public static bool IsBasePropertyDefinition(this PropertyInfo propertyInfo)
{
foreach (var accessor in propertyInfo.GetAccessors())
{
if (!accessor.IsVirtual)
{
continue;
}
if (accessor.GetBaseDefinition() != accessor)
{
return false;
}
}
return true;
}
public static IEnumerable<Type> GetClassHierarchy(this Type t)
{
yield return t;
while (t.BaseType != null)
{
t = t.BaseType;
yield return t;
}
}
/// <summary>
/// Returns ALL nested types of the specified type, including private types of its parent.
/// </summary>
public static IEnumerable<Type> GetAllNestedTypes(this Type t)
{
foreach (var p in GetClassHierarchy(t))
{
foreach (var field in p.GetNestedTypes(
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.DeclaredOnly |
BindingFlags.Public))
{
yield return field;
}
}
}
internal static readonly IComparer<Type> TypeInheritanceComparer = new TypeInheritanceComparerImpl();
public sealed class TypeInheritanceComparerImpl : IComparer<Type>
{
public int Compare(Type? x, Type? y)
{
if (x == null || y == null || x == y)
{
return 0;
}
if (x.IsAssignableFrom(y))
{
return -1;
}
if (y.IsAssignableFrom(x))
{
return 1;
}
return 0;
}
}
internal static IEnumerable<AbstractFieldInfo> GetAllPropertiesAndFields(this Type type)
{
foreach (var field in type.GetAllFields())
{
yield return new SpecificFieldInfo(field);
}
foreach (var property in type.GetAllProperties())
{
yield return new SpecificPropertyInfo(property);
}
}
public static bool TrySelectCommonType(Type type1, Type type2, [NotNullWhen(true)] out Type? commonType)
{
commonType = null;
if (type1.IsAssignableFrom(type2))
{
commonType = type1;
}
else if (type2.IsAssignableFrom(type1))
{
commonType = type2;
}
return commonType != null;
}
internal static SpecificFieldInfo? GetBackingField(this Type type, string propertyName)
{
foreach (var parent in type.GetClassHierarchy())
{
var field = parent.GetField($"<{propertyName}>k__BackingField",
BindingFlags.NonPublic | BindingFlags.Instance);
if (field != null)
{
return new SpecificFieldInfo(field);
}
}
return null;
}
public static bool HasBackingField(this Type type, string propertyName)
{
return type.GetBackingField(propertyName) != null;
}
internal static bool TryGetBackingField(this Type type, string propertyName,
[NotNullWhen(true)] out SpecificFieldInfo? field)
{
return (field = type.GetBackingField(propertyName)) != null;
}
public static bool IsBackingField(this MemberInfo memberInfo)
{
return memberInfo.HasCustomAttribute<CompilerGeneratedAttribute>() &&
memberInfo.Name.StartsWith("<") &&
memberInfo.Name.EndsWith(">k__BackingField");
}
public static bool HasCustomAttribute<T>(this MemberInfo memberInfo) where T : Attribute
{
return memberInfo.GetCustomAttribute<T>() != null;
}
public static bool HasCustomAttribute<T>(this ParameterInfo memberInfo) where T : Attribute
{
return memberInfo.GetCustomAttribute<T>() != null;
}
public static bool TryGetCustomAttribute<T>(this MemberInfo memberInfo, [NotNullWhen(true)] out T? attribute)
where T : Attribute
{
return (attribute = memberInfo.GetCustomAttribute<T>()) != null;
}
public static bool TryGetCustomAttribute(this MemberInfo memberInfo, Type type, [NotNullWhen(true)] out Attribute? attribute)
{
return (attribute = memberInfo.GetCustomAttribute(type)) != null;
}
internal static bool HasParameterlessConstructor(this Type type, BindingFlags flags = BindingFlags.Instance | BindingFlags.Public)
{
return type
.GetConstructors(flags)
.Any(m => m.GetParameters().Length == 0);
}
internal static bool IsAutogeneratedRecordMember(this AbstractFieldInfo info)
{
if (info.DeclaringType == null)
return false;
if (info.Name == "EqualityContract" && info.FieldType == typeof(Type))
return true;
return false;
}
}
}