Created
December 23, 2024 23:07
-
-
Save AArnott/acaca32ab812b9f97fd2f5fc7301b1e9 to your computer and use it in GitHub Desktop.
Reflect over generic attributes on .NET Framework
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Reflection.Metadata; | |
class CustomAttributeTypeProvider : ICustomAttributeTypeProvider<Type> | |
{ | |
internal static readonly CustomAttributeTypeProvider Instance = new(); | |
public Type GetPrimitiveType(PrimitiveTypeCode typeCode) | |
{ | |
return SignatureTypeProvider.Instance.GetPrimitiveType(typeCode); | |
} | |
public Type GetSystemType() | |
{ | |
throw new NotImplementedException(); | |
} | |
public Type GetSZArrayType(Type elementType) | |
{ | |
throw new NotImplementedException(); | |
} | |
public Type GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) | |
{ | |
throw new NotImplementedException(); | |
} | |
public Type GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) | |
{ | |
throw new NotImplementedException(); | |
} | |
public Type GetTypeFromSerializedName(string name) | |
{ | |
throw new NotImplementedException(); | |
} | |
public PrimitiveTypeCode GetUnderlyingEnumType(Type type) | |
{ | |
throw new NotImplementedException(); | |
} | |
public bool IsSystemType(Type type) | |
{ | |
throw new NotImplementedException(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Reflection; | |
using System.Reflection.Metadata; | |
using System.Reflection.Metadata.Ecma335; | |
using System.Reflection.PortableExecutable; | |
[MyAttribute<int>("hi", NamedProp = 3)] | |
public class C | |
{ | |
static void Main() | |
{ | |
foreach (Attribute a in typeof(C).GetCustomAttributesAllowGeneric()) | |
{ | |
Console.WriteLine(a); | |
} | |
} | |
} | |
[AttributeUsage(AttributeTargets.Class)] | |
public class MyAttribute<T>(string arg) : Attribute | |
{ | |
public T NamedProp { get; set; } | |
public override string ToString() => $"{arg} {NamedProp}"; | |
} | |
internal static class Utility | |
{ | |
internal static IEnumerable<Attribute> GetCustomAttributesAllowGeneric(this MemberInfo member) | |
{ | |
using PEReader peReader = new PEReader(File.OpenRead(member.Module.Assembly.Location)); | |
MetadataReader mdReader = peReader.GetMetadataReader(); | |
EntityHandle attributedMemberHandle = MetadataTokens.EntityHandle(member.MetadataToken); | |
foreach (CustomAttributeHandle cah in mdReader.GetCustomAttributes(attributedMemberHandle)) | |
{ | |
CustomAttribute att = mdReader.GetCustomAttribute(cah); | |
int attCtorToken = MetadataTokens.GetToken(att.Constructor); | |
Type constructedAttributeType; | |
switch (att.Constructor.Kind) | |
{ | |
case HandleKind.MemberReference: | |
MemberReference mr = mdReader.GetMemberReference((MemberReferenceHandle)att.Constructor); | |
MethodSignature<Type> sig = mr.DecodeMethodSignature(SignatureTypeProvider.Instance, new GenericContext()); | |
switch (mr.Parent.Kind) | |
{ | |
case HandleKind.TypeSpecification: | |
TypeSpecification attTypeSpec = mdReader.GetTypeSpecification((TypeSpecificationHandle)mr.Parent); | |
constructedAttributeType = attTypeSpec.DecodeSignature(SignatureTypeProvider.Instance, default); | |
break; | |
default: | |
throw new NotSupportedException(); | |
} | |
break; | |
case HandleKind.MethodDefinition: | |
MethodDefinition attCtor = mdReader.GetMethodDefinition((MethodDefinitionHandle)att.Constructor); | |
TypeDefinition attClass = mdReader.GetTypeDefinition(attCtor.GetDeclaringType()); | |
Console.WriteLine(mdReader.GetString(attClass.Name)); | |
throw new NotImplementedException(); | |
break; | |
default: | |
throw new NotSupportedException(); | |
} | |
CustomAttributeValue<Type> attValue = att.DecodeValue(CustomAttributeTypeProvider.Instance); | |
ConstructorInfo attCtorInfo = (ConstructorInfo)constructedAttributeType.Module.ResolveMethod(attCtorToken); | |
Attribute attObject = (Attribute)attCtorInfo.Invoke(GetAttributeConstructorArguments(attValue)); | |
// Set properties | |
foreach (CustomAttributeNamedArgument<Type> namedArg in attValue.NamedArguments) | |
{ | |
PropertyInfo prop = constructedAttributeType.GetProperty(namedArg.Name); | |
prop.SetValue(attObject, namedArg.Value); | |
} | |
yield return attObject; | |
} | |
} | |
private static object?[] GetAttributeConstructorArguments(CustomAttributeValue<Type> attValue) | |
{ | |
object?[] args = new object[attValue.FixedArguments.Length]; | |
for (int i = 0; i < attValue.FixedArguments.Length; i++) | |
{ | |
CustomAttributeTypedArgument<Type> arg = attValue.FixedArguments[i]; | |
args[i] = arg.Value; | |
} | |
return args; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections.Immutable; | |
using System.Reflection; | |
using System.Reflection.Metadata; | |
class SignatureTypeProvider : ISignatureTypeProvider<Type, GenericContext> | |
{ | |
internal static readonly SignatureTypeProvider Instance = new(); | |
public Type GetArrayType(Type elementType, ArrayShape shape) | |
{ | |
throw new NotImplementedException(); | |
} | |
public Type GetByReferenceType(Type elementType) | |
{ | |
throw new NotImplementedException(); | |
} | |
public Type GetFunctionPointerType(MethodSignature<Type> signature) | |
{ | |
throw new NotImplementedException(); | |
} | |
public Type GetGenericInstantiation(Type genericType, ImmutableArray<Type> typeArguments) | |
{ | |
return genericType.MakeGenericType(typeArguments.ToArray()); | |
} | |
public Type GetGenericMethodParameter(GenericContext genericContext, int index) | |
{ | |
throw new NotImplementedException(); | |
} | |
public Type GetGenericTypeParameter(GenericContext genericContext, int index) | |
{ | |
throw new NotImplementedException(); | |
} | |
public Type GetModifiedType(Type modifier, Type unmodifiedType, bool isRequired) | |
{ | |
throw new NotImplementedException(); | |
} | |
public Type GetPinnedType(Type elementType) | |
{ | |
throw new NotImplementedException(); | |
} | |
public Type GetPointerType(Type elementType) | |
{ | |
throw new NotImplementedException(); | |
} | |
public Type GetPrimitiveType(PrimitiveTypeCode typeCode) | |
{ | |
return typeCode switch | |
{ | |
PrimitiveTypeCode.Boolean => typeof(bool), | |
PrimitiveTypeCode.Byte => typeof(byte), | |
PrimitiveTypeCode.SByte => typeof(sbyte), | |
PrimitiveTypeCode.Char => typeof(char), | |
PrimitiveTypeCode.Int16 => typeof(short), | |
PrimitiveTypeCode.UInt16 => typeof(ushort), | |
PrimitiveTypeCode.Int32 => typeof(int), | |
PrimitiveTypeCode.UInt32 => typeof(uint), | |
PrimitiveTypeCode.Int64 => typeof(long), | |
PrimitiveTypeCode.UInt64 => typeof(ulong), | |
PrimitiveTypeCode.Single => typeof(float), | |
PrimitiveTypeCode.Double => typeof(double), | |
PrimitiveTypeCode.IntPtr => typeof(IntPtr), | |
PrimitiveTypeCode.UIntPtr => typeof(UIntPtr), | |
PrimitiveTypeCode.Object => typeof(object), | |
PrimitiveTypeCode.String => typeof(string), | |
PrimitiveTypeCode.Void => typeof(void), | |
PrimitiveTypeCode.TypedReference => throw new NotImplementedException(), | |
_ => throw new NotImplementedException(), | |
}; | |
} | |
public Type GetSZArrayType(Type elementType) | |
{ | |
throw new NotImplementedException(); | |
} | |
public Type GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) | |
{ | |
TypeDefinition typeDef = reader.GetTypeDefinition(handle); | |
Assembly assembly = Assembly.Load(reader.GetAssemblyDefinition().GetAssemblyName()); | |
Type type = assembly.GetType(reader.GetString(typeDef.Name)); | |
return type; | |
} | |
public Type GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) | |
{ | |
throw new NotImplementedException(); | |
} | |
public Type GetTypeFromSpecification(MetadataReader reader, GenericContext genericContext, TypeSpecificationHandle handle, byte rawTypeKind) | |
{ | |
throw new NotImplementedException(); | |
} | |
} | |
struct GenericContext { } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment