mirror of
https://github.com/AssetRipper/AssetRipper.git
synced 2025-12-11 20:15:29 +01:00
508 lines
20 KiB
C#
508 lines
20 KiB
C#
using AssetRipper.AssemblyDumper.Methods;
|
|
using AssetRipper.AssemblyDumper.Types;
|
|
using AssetRipper.Assets.Generics;
|
|
using System.Text;
|
|
|
|
namespace AssetRipper.AssemblyDumper.Passes;
|
|
|
|
internal static class Pass052_InterfacePropertiesAndMethods
|
|
{
|
|
private static readonly SignatureComparer signatureComparer = new(SignatureComparisonFlags.VersionAgnostic);
|
|
|
|
public static void DoPass()
|
|
{
|
|
foreach (ClassGroupBase group in SharedState.Instance.AllGroups)
|
|
{
|
|
group.ImplementProperties();
|
|
}
|
|
}
|
|
|
|
private static void ImplementProperties(this ClassGroupBase group)
|
|
{
|
|
Dictionary<string, (string, TypeSignature, bool)> propertyDictionary = group.GetPropertyDictionary();
|
|
HashSet<string> differingFieldNames = group.GetDifferingFieldNames();
|
|
|
|
foreach ((string propertyName, (string fieldName, TypeSignature propertyTypeSignature, bool hasConflictingTypes)) in propertyDictionary)
|
|
{
|
|
bool missingOnSomeVersions = differingFieldNames.Contains(fieldName);
|
|
bool isValueType = propertyTypeSignature.IsValueType;
|
|
|
|
PropertyDefinition propertyDeclaration = group.AddInterfacePropertyDeclaration(propertyName, propertyTypeSignature);
|
|
InterfaceProperty interfaceProperty = new InterfaceProperty(propertyDeclaration, group);
|
|
group.InterfaceProperties.Add(interfaceProperty);
|
|
|
|
foreach (GeneratedClassInstance instance in group.Instances)
|
|
{
|
|
FieldDefinition? field = instance.Type.TryGetFieldByName(fieldName, true);
|
|
PropertyDefinition property;
|
|
ClassProperty classProperty;
|
|
if (hasConflictingTypes || missingOnSomeVersions)
|
|
{
|
|
if (field is not { Signature.FieldType: { } fieldType } || !instance.Class.ContainsField(fieldName))
|
|
{
|
|
// Field either:
|
|
// * doesn't exist
|
|
// * exists in a base class, but isn't present in this derived class
|
|
property = instance.ImplementInterfaceProperty(interfaceProperty, null);
|
|
classProperty = new ClassProperty(property, null, interfaceProperty, instance);
|
|
}
|
|
else if (!hasConflictingTypes || signatureComparer.Equals(fieldType, propertyTypeSignature))
|
|
{
|
|
property = instance.ImplementInterfaceProperty(interfaceProperty, field);
|
|
classProperty = new ClassProperty(property, field, interfaceProperty, instance);
|
|
|
|
if (propertyTypeSignature is SzArrayTypeSignature)
|
|
{
|
|
property.FixNullableArraySetMethod(field);
|
|
}
|
|
}
|
|
else if (interfaceProperty.HasSetAccessor && fieldType.IsIntegerPrimitive(out ElementType fieldPrimitive) && propertyTypeSignature.IsIntegerPrimitive(out ElementType propertyPrimitive))
|
|
{
|
|
// Field exists, but is the wrong primitive type, so we convert.
|
|
// Because we check HasSetAccessor, this does not run on PPtrs, which is fine.
|
|
property = instance.Type.AddFullProperty(propertyName, InterfaceUtils.InterfacePropertyImplementation, propertyTypeSignature);
|
|
|
|
// get
|
|
{
|
|
CilInstructionCollection instructions = property.GetMethod!.CilMethodBody!.Instructions;
|
|
instructions.Add(CilOpCodes.Ldarg_0);
|
|
instructions.Add(CilOpCodes.Ldfld, field);
|
|
instructions.Add(propertyPrimitive switch
|
|
{
|
|
ElementType.U1 => CilOpCodes.Conv_U1,
|
|
ElementType.U2 => CilOpCodes.Conv_U2,
|
|
ElementType.U4 => CilOpCodes.Conv_U4,
|
|
ElementType.U => CilOpCodes.Conv_U,
|
|
ElementType.U8 => CilOpCodes.Conv_U8,
|
|
ElementType.I1 => CilOpCodes.Conv_I1,
|
|
ElementType.I2 => CilOpCodes.Conv_I2,
|
|
ElementType.I4 => CilOpCodes.Conv_I4,
|
|
ElementType.I => CilOpCodes.Conv_I,
|
|
ElementType.I8 => CilOpCodes.Conv_I8,
|
|
_ => throw new NotSupportedException(),
|
|
});
|
|
instructions.Add(CilOpCodes.Ret);
|
|
}
|
|
|
|
// set
|
|
{
|
|
CilInstructionCollection instructions = property.SetMethod!.CilMethodBody!.Instructions;
|
|
instructions.Add(CilOpCodes.Ldarg_0);
|
|
instructions.Add(CilOpCodes.Ldarg_1);
|
|
instructions.Add(fieldPrimitive switch
|
|
{
|
|
ElementType.U1 => CilOpCodes.Conv_U1,
|
|
ElementType.U2 => CilOpCodes.Conv_U2,
|
|
ElementType.U4 => CilOpCodes.Conv_U4,
|
|
ElementType.U => CilOpCodes.Conv_U,
|
|
ElementType.U8 => CilOpCodes.Conv_U8,
|
|
ElementType.I1 => CilOpCodes.Conv_I1,
|
|
ElementType.I2 => CilOpCodes.Conv_I2,
|
|
ElementType.I4 => CilOpCodes.Conv_I4,
|
|
ElementType.I => CilOpCodes.Conv_I,
|
|
ElementType.I8 => CilOpCodes.Conv_I8,
|
|
_ => throw new NotSupportedException(),
|
|
});
|
|
instructions.Add(CilOpCodes.Stfld, field);
|
|
instructions.Add(CilOpCodes.Ret);
|
|
}
|
|
|
|
// We give a null backing field here because it doesn't actually have one.
|
|
// The other property is the one that actually has a backing field.
|
|
classProperty = new ClassProperty(property, null, interfaceProperty, instance);
|
|
}
|
|
else
|
|
{
|
|
// Field is not compatible with the property
|
|
property = instance.ImplementInterfaceProperty(interfaceProperty, null);
|
|
classProperty = new ClassProperty(property, null, interfaceProperty, instance);
|
|
}
|
|
|
|
instance.Properties.Add(classProperty);
|
|
}
|
|
else
|
|
{
|
|
property = instance.ImplementInterfaceProperty(interfaceProperty, field);
|
|
classProperty = new ClassProperty(property, field, interfaceProperty, instance);
|
|
instance.Properties.Add(classProperty);
|
|
}
|
|
|
|
if (instance.Type.IsAbstract || instance.Group.ID is 2)
|
|
{
|
|
property.AddDebuggerBrowsableNeverAttribute();//Properties in base classes are redundant in the debugger.
|
|
}
|
|
else if (classProperty.IsAbsent)
|
|
{
|
|
property.AddDebuggerBrowsableNeverAttribute();//Dummy properties should not be visible in the debugger.
|
|
}
|
|
else if (classProperty.Name == "Name_R")
|
|
{
|
|
property.AddDebuggerBrowsableNeverAttribute();//Name_R is redundant in the debugger because a Name property exists.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static HashSet<string> GetDifferingFieldNames(this ClassGroupBase group)
|
|
{
|
|
List<(GeneratedClassInstance, List<string>)> data = new();
|
|
List<string> allFieldNames = new();
|
|
foreach (GeneratedClassInstance instance in group.Instances)
|
|
{
|
|
List<string> instanceFieldNames = instance.Class.GetFieldNames().ToList();
|
|
data.Add((instance, instanceFieldNames));
|
|
allFieldNames.AddRange(instanceFieldNames);
|
|
}
|
|
return allFieldNames.Distinct().Where(f => data.Any(pair => !pair.Item2.Contains(f))).ToHashSet();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Field name : List of field types
|
|
/// </summary>
|
|
private static Dictionary<string, List<TypeSignature>> GetFieldTypeListDictionary(this ClassGroupBase group)
|
|
{
|
|
Dictionary<string, List<TypeSignature>> result = new();
|
|
foreach (GeneratedClassInstance instance in group.Instances)
|
|
{
|
|
foreach (string fieldName in instance.Class.GetFieldNames())
|
|
{
|
|
TypeSignature fieldType = instance.Type.GetFieldByName(fieldName, true).Signature!.FieldType;
|
|
List<TypeSignature> typeList = result.GetOrAdd(fieldName);
|
|
if (!typeList.Any(sig => signatureComparer.Equals(sig, fieldType)))
|
|
{
|
|
typeList.Add(fieldType);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static Dictionary<string, T> SortStringDictionary<T>(this Dictionary<string, T> dictionary)
|
|
{
|
|
var keyList = dictionary.Keys.ToList();
|
|
keyList.Sort();
|
|
return keyList.ToDictionary(key => key, key => dictionary[key]);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Property name : field name, property type, type conflict
|
|
/// </summary>
|
|
private static Dictionary<string, (string, TypeSignature, bool)> GetPropertyDictionary(this ClassGroupBase group)
|
|
{
|
|
Dictionary<string, List<TypeSignature>> fieldTypeDictionary = group.GetFieldTypeListDictionary().SortStringDictionary();
|
|
Dictionary<string, (string, TypeSignature, bool)> propertyDictionary = new();
|
|
|
|
foreach ((string fieldName, List<TypeSignature> fieldTypeList) in fieldTypeDictionary)
|
|
{
|
|
string propertyName = GeneratedInterfaceUtils.GetPropertyNameFromFieldName(fieldName, group);
|
|
if (fieldTypeList.Count == 1)
|
|
{
|
|
propertyDictionary.Add(propertyName, (fieldName, fieldTypeList[0], false));
|
|
}
|
|
else if (TryGetCommonInheritor(fieldTypeList, out TypeSignature? baseInterface))
|
|
{
|
|
propertyDictionary.Add(propertyName, (fieldName, baseInterface, false));
|
|
}
|
|
else if (TryGetCommonGenericInstance(fieldTypeList, out TypeSignature? accessTypeSignature))
|
|
{
|
|
propertyDictionary.Add(propertyName, (fieldName, accessTypeSignature, false));
|
|
}
|
|
else
|
|
{
|
|
foreach (TypeSignature fieldType in fieldTypeList)
|
|
{
|
|
string fieldTypeName = GetName(fieldType);
|
|
propertyDictionary.Add($"{propertyName}_{fieldTypeName}", (fieldName, fieldType, true));
|
|
}
|
|
}
|
|
}
|
|
|
|
return propertyDictionary;
|
|
}
|
|
|
|
private static bool TryGetCommonGenericInstance(List<TypeSignature> fieldTypeList, [NotNullWhen(true)] out TypeSignature? accessTypeSignature)
|
|
{
|
|
accessTypeSignature = null;
|
|
if (fieldTypeList.TryCast(out List<GenericInstanceTypeSignature>? genericInstanceFields))
|
|
{
|
|
if (genericInstanceFields.All(genericInstance => genericInstance.GenericType.Name == "AssetList`1"))
|
|
{
|
|
List<TypeSignature> typeArguments = genericInstanceFields.Select(genericInstance => genericInstance.TypeArguments.Single()).ToList();
|
|
if (TryGetCommonInheritor(typeArguments, out TypeSignature? commonInterface))
|
|
{
|
|
accessTypeSignature = SharedState.Instance.Importer.ImportTypeSignature(typeof(AccessListBase<>)).MakeGenericInstanceType(commonInterface);
|
|
}
|
|
}
|
|
else if (genericInstanceFields.All(genericInstance => genericInstance.GenericType.Name == "AssetDictionary`2"))
|
|
{
|
|
List<TypeSignature> keyTypeArguments = genericInstanceFields.Select(genericInstance => genericInstance.TypeArguments[0]).ToList();
|
|
List<TypeSignature> valueTypeArguments = genericInstanceFields.Select(genericInstance => genericInstance.TypeArguments[1]).ToList();
|
|
if (keyTypeArguments.TryGetEqualityOrCommonInheritor(out TypeSignature? commonKeyType)
|
|
&& valueTypeArguments.TryGetEqualityOrCommonInheritor(out TypeSignature? commonValueType))
|
|
{
|
|
accessTypeSignature = SharedState.Instance.Importer.ImportTypeSignature(typeof(AccessDictionaryBase<,>))
|
|
.MakeGenericInstanceType(commonKeyType, commonValueType);
|
|
}
|
|
}
|
|
//Pair only used by Sprite and it's AssetPair<GUID, long>
|
|
}
|
|
return accessTypeSignature != null;
|
|
}
|
|
|
|
private static bool TryGetEqualityOrCommonInheritor(this List<TypeSignature> types, [NotNullWhen(true)] out TypeSignature? commonType)
|
|
{
|
|
return types.TryGetEquality(out commonType) || types.TryGetCommonInheritor(out commonType);
|
|
}
|
|
|
|
private static bool TryGetEquality(this List<TypeSignature> types, [NotNullWhen(true)] out TypeSignature? commonType)
|
|
{
|
|
TypeSignature first = types.First();
|
|
if (types.All(type => signatureComparer.Equals(type, first)))
|
|
{
|
|
commonType = first;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
commonType = null;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static bool TryGetCommonInheritor(this List<TypeSignature> types, [NotNullWhen(true)] out TypeSignature? baseInterface)
|
|
{
|
|
if (types.Count == 0)
|
|
{
|
|
throw new ArgumentException(null, nameof(types));
|
|
}
|
|
|
|
if (TryGetTypeDefinitionsForTypeSignatures(types, out List<TypeDefinition>? typeDefinitions))
|
|
{
|
|
ClassGroupBase group = SharedState.Instance.TypesToGroups[typeDefinitions[0]];
|
|
if (!typeDefinitions.Any(def => !group.ContainsTypeDefinition(def)))
|
|
{
|
|
baseInterface = group.GetSingularTypeOrInterface().ToTypeSignature();
|
|
return true;
|
|
}
|
|
}
|
|
baseInterface = null;
|
|
return false;
|
|
}
|
|
|
|
private static bool ContainsTypeDefinition(this ClassGroupBase group, TypeDefinition type)
|
|
{
|
|
//any instance where an instance's type definition is equal or the group interface is equal
|
|
return group.Instances.Any(instance => signatureComparer.Equals(type, instance.Type)) || signatureComparer.Equals(type, group.Interface);
|
|
}
|
|
|
|
private static bool TryGetTypeDefinitionForTypeSignature(TypeSignature typeSignature, [NotNullWhen(true)] out TypeDefinition? typeDefinition)
|
|
{
|
|
typeDefinition = (typeSignature as TypeDefOrRefSignature)?.Type as TypeDefinition;
|
|
return typeDefinition != null;
|
|
}
|
|
|
|
private static TypeDefinition? TryGetTypeDefinitionForTypeSignature(TypeSignature typeSignature)
|
|
{
|
|
return (typeSignature as TypeDefOrRefSignature)?.Type as TypeDefinition;
|
|
}
|
|
|
|
private static bool TryGetTypeDefinitionsForTypeSignatures(List<TypeSignature> typeSignatures, [NotNullWhen(true)] out List<TypeDefinition>? typeDefinitions)
|
|
{
|
|
typeDefinitions = new List<TypeDefinition>(typeSignatures.Count);
|
|
foreach (TypeSignature typeSignature in typeSignatures)
|
|
{
|
|
if (TryGetTypeDefinitionForTypeSignature(typeSignature, out TypeDefinition? typeDefinition))
|
|
{
|
|
typeDefinitions.Add(typeDefinition);
|
|
}
|
|
else
|
|
{
|
|
typeDefinitions = null;
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static PropertyDefinition AddInterfacePropertyDeclaration(this ClassGroupBase group, string propertyName, TypeSignature propertyType)
|
|
{
|
|
return !group.IsPPtr && ShouldUseFullProperty(propertyType)
|
|
? group.Interface.AddFullProperty(propertyName, InterfaceUtils.InterfacePropertyDeclaration, propertyType)
|
|
: group.Interface.AddGetterProperty(propertyName, InterfaceUtils.InterfacePropertyDeclaration, propertyType);
|
|
}
|
|
|
|
private static bool ShouldUseFullProperty(TypeSignature propertyType)
|
|
{
|
|
return propertyType is SzArrayTypeSignature or CorLibTypeSignature || propertyType.IsUtf8String() || propertyType.IsValueType;
|
|
}
|
|
|
|
private static PropertyDefinition ImplementInterfaceProperty(this GeneratedClassInstance instance, InterfaceProperty interfaceProperty, FieldDefinition? field)
|
|
{
|
|
TypeDefinition declaringType = instance.Type;
|
|
string propertyName = interfaceProperty.Name;
|
|
TypeSignature propertyType = interfaceProperty.Definition.Signature!.ReturnType;
|
|
if (interfaceProperty.HasSetAccessor)
|
|
{
|
|
return declaringType.ImplementFullProperty(propertyName, InterfaceUtils.InterfacePropertyImplementation, propertyType, field);
|
|
}
|
|
else if (field is not null && propertyType is GenericInstanceTypeSignature genericPropertyType)
|
|
{
|
|
IMethodDefOrRef constructor;
|
|
switch (genericPropertyType.GenericType.Name?.Value)
|
|
{
|
|
case "AccessListBase`1":
|
|
{
|
|
GenericInstanceTypeSignature fieldType = field.Signature?.FieldType as GenericInstanceTypeSignature ?? throw new();
|
|
TypeSignature underlyingType = fieldType.TypeArguments.Single();
|
|
TypeSignature baseType = genericPropertyType.TypeArguments.Single();
|
|
GenericInstanceTypeSignature accessListSignature = SharedState.Instance.Importer
|
|
.ImportTypeSignature(typeof(AccessList<,>))
|
|
.MakeGenericInstanceType(underlyingType, baseType);
|
|
constructor = MethodUtils.MakeConstructorOnGenericType(SharedState.Instance.Importer, accessListSignature, 1);
|
|
}
|
|
break;
|
|
case "AccessDictionaryBase`2":
|
|
{
|
|
GenericInstanceTypeSignature fieldType = field.Signature?.FieldType as GenericInstanceTypeSignature ?? throw new();
|
|
TypeSignature keyNormalType = fieldType.TypeArguments[0];
|
|
TypeSignature valueNormalType = fieldType.TypeArguments[1];
|
|
TypeSignature keyBaseType = genericPropertyType.TypeArguments[0];
|
|
TypeSignature valueBaseType = genericPropertyType.TypeArguments[1];
|
|
GenericInstanceTypeSignature accessDictionarySignature = SharedState.Instance.Importer
|
|
.ImportTypeSignature(typeof(AccessDictionary<,,,>))
|
|
.MakeGenericInstanceType(keyNormalType, valueNormalType, keyBaseType, valueBaseType);
|
|
constructor = MethodUtils.MakeConstructorOnGenericType(SharedState.Instance.Importer, accessDictionarySignature, 1);
|
|
}
|
|
break;
|
|
case "AccessPairBase`2":
|
|
//Pair only used by Sprite and it's AssetPair<GUID, long>
|
|
throw new NotSupportedException("AccessPair not supported.");
|
|
default:
|
|
//Handles AssetList, AssetDictionary, and AssetPair
|
|
return declaringType.ImplementGetterProperty(propertyName, InterfaceUtils.InterfacePropertyImplementation, propertyType, field);
|
|
}
|
|
PropertyDefinition property = declaringType.AddGetterProperty(propertyName, InterfaceUtils.InterfacePropertyImplementation, propertyType);
|
|
FieldDefinition cacheField = declaringType
|
|
.AddField($"_{propertyName}_k__BackingField", propertyType, visibility: Visibility.Private)
|
|
.AddNullableAttributesForMaybeNull();
|
|
cacheField.AddDebuggerBrowsableNeverAttribute();
|
|
CilInstructionCollection instructions = property.GetMethod!.CilMethodBody!.Instructions;
|
|
|
|
CilInstructionLabel afterAssignmentLabel = new();
|
|
|
|
//if accessValue is null
|
|
instructions.Add(CilOpCodes.Ldarg_0);
|
|
instructions.Add(CilOpCodes.Ldfld, cacheField);
|
|
instructions.Add(CilOpCodes.Ldnull);
|
|
instructions.Add(CilOpCodes.Ceq);
|
|
instructions.Add(CilOpCodes.Brfalse_S, afterAssignmentLabel);
|
|
|
|
//accessValue = new(referenceValue);
|
|
instructions.Add(CilOpCodes.Ldarg_0);
|
|
instructions.Add(CilOpCodes.Ldarg_0);
|
|
instructions.Add(CilOpCodes.Ldfld, field);
|
|
instructions.Add(CilOpCodes.Newobj, constructor);
|
|
instructions.Add(CilOpCodes.Stfld, cacheField);
|
|
|
|
//return accessValue
|
|
afterAssignmentLabel.Instruction = instructions.Add(CilOpCodes.Nop);
|
|
instructions.Add(CilOpCodes.Ldarg_0);
|
|
instructions.Add(CilOpCodes.Ldfld, cacheField);
|
|
instructions.Add(CilOpCodes.Ret);
|
|
return property;
|
|
}
|
|
else
|
|
{
|
|
return declaringType.ImplementGetterProperty(propertyName, InterfaceUtils.InterfacePropertyImplementation, propertyType, field);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ensures that the <paramref name="field"/> is set to <see cref="Array.Empty{T}"/> instead of null.
|
|
/// </summary>
|
|
private static void FixNullableArraySetMethod(this PropertyDefinition property, FieldDefinition field)
|
|
{
|
|
TypeSignature elementType = ((SzArrayTypeSignature)field.Signature!.FieldType).BaseType;
|
|
MethodSpecification emptyArrayMethod = SharedState.Instance.Importer
|
|
.ImportMethod(typeof(Array), m => m.Name == nameof(Array.Empty))
|
|
.MakeGenericInstanceMethod(elementType);
|
|
|
|
CilInstructionCollection instructions = property.SetMethod!.CilMethodBody!.Instructions;
|
|
instructions.Clear();
|
|
instructions.Add(CilOpCodes.Ldarg_0);
|
|
instructions.Add(CilOpCodes.Ldarg_1); //value
|
|
|
|
CilInstructionLabel label = new();
|
|
instructions.Add(CilOpCodes.Dup);
|
|
instructions.Add(CilOpCodes.Brtrue, label);
|
|
instructions.Add(CilOpCodes.Pop);
|
|
instructions.Add(CilOpCodes.Call, emptyArrayMethod);
|
|
|
|
label.Instruction = instructions.Add(CilOpCodes.Stfld, field);
|
|
instructions.Add(CilOpCodes.Ret);
|
|
instructions.OptimizeMacros();
|
|
}
|
|
|
|
private static IEnumerable<string> GetFieldNames(this UniversalClass universalClass)
|
|
{
|
|
return universalClass.EditorRootNode.GetFieldNames().Union(universalClass.ReleaseRootNode.GetFieldNames()).Distinct();
|
|
}
|
|
|
|
private static IEnumerable<string> GetFieldNames(this UniversalNode? rootNode)
|
|
{
|
|
return rootNode?.SubNodes.Select(node => node.Name) ?? Enumerable.Empty<string>();
|
|
}
|
|
|
|
private static string GetName(TypeSignature type)
|
|
{
|
|
if (type is CorLibTypeSignature)
|
|
{
|
|
return type.Name ?? throw new NullReferenceException();
|
|
}
|
|
else if (type is TypeDefOrRefSignature normalType)
|
|
{
|
|
string asmName = normalType.Name;
|
|
int index = asmName.IndexOf('`');
|
|
return index > -1 ? asmName.Substring(0, index) : asmName;
|
|
}
|
|
else if (type is SzArrayTypeSignature arrayType)
|
|
{
|
|
return $"{GetName(arrayType.BaseType)}_Array";
|
|
}
|
|
else if (type is GenericInstanceTypeSignature genericInstanceType)
|
|
{
|
|
string baseTypeName = GetName(genericInstanceType.GenericType.ToTypeSignature());
|
|
StringBuilder sb = new();
|
|
sb.Append(baseTypeName);
|
|
foreach (TypeSignature typeArgument in genericInstanceType.TypeArguments)
|
|
{
|
|
sb.Append('_');
|
|
sb.Append(GetName(typeArgument));
|
|
}
|
|
return sb.ToString();
|
|
}
|
|
else
|
|
{
|
|
throw new NotSupportedException($"GetName not support for {type.FullName} of type {type.GetType()}");
|
|
}
|
|
}
|
|
|
|
private static bool TryCast<T, TCast>(this List<T> originalList, [NotNullWhen(true)] out List<TCast>? castedList)
|
|
{
|
|
castedList = new List<TCast>(originalList.Count);
|
|
foreach (T element in originalList)
|
|
{
|
|
if (element is TCast castedElement)
|
|
{
|
|
castedList.Add(castedElement);
|
|
}
|
|
else
|
|
{
|
|
castedList = null;
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|