是否存在兼容反射的元数据读取器?适配.NET Framework 4.8
获取.NET Framework 4.8程序集声明式安全属性的优化方案
1. 用原生兼容的元数据读取类直接操作DeclSecurity表
.NET Framework 4.8可以借助NuGet包System.Reflection.Metadata和System.Reflection.PortableExecutable直接解析元数据,不用依赖第三方工具,还能和现有反射对象无缝结合:
- 先加载目标程序集,遍历其模块;
- 通过
PEReader读取PE文件,获取MetadataReader来访问DeclSecurity表; - 解析表中每条记录的目标(类型、方法、字段等)和权限集Blob,再转成可识别的
PermissionSet对象。
示例代码:
using System.Reflection; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Security.Permissions; using System.Text; // 加载目标程序集 var targetAssembly = Assembly.LoadFrom("YourAssembly.dll"); foreach (var module in targetAssembly.Modules) { // 读取模块的PE流 using var stream = module.Assembly.GetManifestResourceStream(module.Name); using var peReader = new PEReader(stream); if (!peReader.HasMetadata) continue; var reader = peReader.GetMetadataReader(); // 遍历DeclSecurity元数据表 foreach (var handle in reader.DeclarativeSecurityAttributes) { var decSec = reader.GetDeclarativeSecurityAttribute(handle); // 获取属性关联的目标(类型、方法等) var targetHandle = decSec.Parent; var targetName = GetTargetDisplayName(reader, targetHandle); // 解析权限集Blob var blob = reader.GetBlobBytes(decSec.PermissionSet); var permissionSet = PermissionSet.FromXml(Encoding.UTF8.GetString(blob)); Console.WriteLine($"目标: {targetName}, 权限内容: {permissionSet}"); } } // 辅助方法:根据元数据句柄获取目标的显示名称 string GetTargetDisplayName(MetadataReader reader, EntityHandle handle) { return handle.Kind switch { HandleKind.TypeDefinition => reader.GetTypeDefinition((TypeDefinitionHandle)handle).Name.ToString(), HandleKind.MethodDefinition => { var method = reader.GetMethodDefinition((MethodDefinitionHandle)handle); var declaringType = reader.GetTypeDefinition(method.GetDeclaringType()); return $"{declaringType.Name}.{method.Name}"; }, HandleKind.FieldDefinition => { var field = reader.GetFieldDefinition((FieldDefinitionHandle)handle); var declaringType = reader.GetTypeDefinition(field.GetDeclaringType()); return $"{declaringType.Name}.{field.Name}"; }, _ => handle.Kind.ToString() }; }
2. 简化dnlib与反射的结合
如果想用dnlib,不用完全切换到它的类体系,通过元数据令牌就能关联dnlib对象和反射对象:
- dnlib里的类型、方法等都有
MDToken属性,反射对象的MetadataToken和这个值完全对应; - 用dnlib读取DeclSecurity表,拿到元数据令牌后,直接用反射解析对应的成员。
示例代码:
using dnlib.DotNet; using System.Reflection; // 加载程序集(dnlib和反射各加载一次) var dnlibModule = ModuleDefMD.Load("YourAssembly.dll"); var reflectionAssembly = Assembly.LoadFrom("YourAssembly.dll"); foreach (var decSec in dnlibModule.DeclarativeSecurityAttributes) { // 通过元数据令牌关联反射成员 var mdToken = decSec.Parent.MDToken.Raw; var reflectionMember = reflectionAssembly.GetType().Module.ResolveMember((int)mdToken); // 直接获取dnlib解析好的权限集 var permissionSet = decSec.PermissionSet; Console.WriteLine($"关联反射成员: {reflectionMember?.Name}, 权限集: {permissionSet}"); }
3. 优化现有反射遍历方案
如果不想直接操作元数据表,也可以优化当前的反射逻辑,提升效率:
- 直接用
GetCustomAttributes的重载指定CodeAccessSecurityAttribute类型,避免遍历所有自定义属性再判断类型; - 分层次获取程序集、类型、成员级别的属性,逻辑更清晰。
示例代码:
using System.Reflection; using System.Security.Permissions; var targetAssembly = Assembly.LoadFrom("YourAssembly.dll"); // 程序集级安全属性 foreach (CodeAccessSecurityAttribute attr in targetAssembly.GetCustomAttributes(typeof(CodeAccessSecurityAttribute), true)) { Console.WriteLine($"程序集属性: {attr.GetType().Name}"); } // 类型级安全属性 foreach (var type in targetAssembly.GetTypes()) { foreach (CodeAccessSecurityAttribute attr in type.GetCustomAttributes(typeof(CodeAccessSecurityAttribute), true)) { Console.WriteLine($"类型 {type.Name} 属性: {attr.GetType().Name}"); } // 成员级安全属性 foreach (var member in type.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)) { foreach (CodeAccessSecurityAttribute attr in member.GetCustomAttributes(typeof(CodeAccessSecurityAttribute), true)) { Console.WriteLine($"成员 {type.Name}.{member.Name} 属性: {attr.GetType().Name}"); } } }
内容的提问来源于stack exchange,提问作者Сергей Гущин
相关产品推荐
相关产品推荐

