
在 C# 中通过反射获取泛型属性例如ListT、DictionaryTKey, TValue或自定义泛型类属性上应用的特性Attribute值与获取普通属性的特性值逻辑基本一致。核心关键点特性通常是应用在属性定义本身PropertyInfo上的而不是应用在属性的具体泛型参数类型上的。因此你只需要获取该属性的PropertyInfo对象然后调用GetCustomAttribute即可。如果你的需求是“我有一个泛型类MyClassT我在它的属性MyProp上加了特性我想在运行时通过反射拿到这个特性的值”以下是完整步骤和代码示例。场景示例假设我们有以下定义一个自定义特性MyCustomAttribute。一个包含泛型属性的类ContainerT。我们需要提取该特性上的数据。1. 定义特性和类using System; using System.Collections.Generic; using System.Reflection; // 1. 定义一个特性 [AttributeUsage(AttributeTargets.Property)] public class MyCustomAttribute : Attribute { public string Description { get; set; } public int Priority { get; set; } public MyCustomAttribute(string description, int priority 0) { Description description; Priority priority; } } // 2. 定义一个包含泛型属性的类 public class ContainerT { // 在泛型属性 ListT 上应用特性 [MyCustomAttribute(这是一个泛型列表, Priority 10)] public ListT Items { get; set; } // 在另一个泛型属性 Dictionarystring, T 上应用特性 [MyCustomAttribute(键值对存储, Priority 5)] public Dictionarystring, T DataMap { get; set; } // 普通非泛型属性作为对比 [MyCustomAttribute(普通属性)] public string Name { get; set; } }2. 通过反射获取特性值无论属性是ListT还是DictionaryK, V获取逻辑都是一样的获取PropertyInfo- 获取Attribute- 读取属性。public class Program { public static void Main() { // 假设我们要检查 ContainerUser 的反射信息 // 注意泛型的具体类型如 User通常不影响属性上特性的读取 // 因为特性是定义在泛型类模板的元数据中的。 Type containerType typeof(Container); // 如果已经构造了具体类型也可以用 typeof(ContainerUser) // 1. 获取所有公共实例属性 PropertyInfo[] properties containerType.GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (var prop in properties) { Console.WriteLine($正在检查属性: {prop.Name} (类型: {prop.PropertyType})); // 2. 获取特定的特性 (MyCustomAttribute) // 使用 GetCustomAttributeT 是最简洁的方式 (.NET 4.5) var customAttr prop.GetCustomAttributeMyCustomAttribute(); if (customAttr ! null) { Console.WriteLine($ - 找到特性!); Console.WriteLine($ 描述: {customAttr.Description}); Console.WriteLine($ 优先级: {customAttr.Priority}); // 额外演示如何判断该属性是否是泛型类型 if (prop.PropertyType.IsGenericType) { Console.WriteLine($ 这是一个泛型属性泛型定义: {prop.PropertyType.GetGenericTypeDefinition()}); Console.WriteLine($ 泛型参数: {string.Join(, , Array.ConvertAll(prop.PropertyType.GetGenericArguments(), t t.Name))}); } } else { Console.WriteLine( - 未找到目标特性); } Console.WriteLine(-------------------------); } } }关键细节解析泛型开放类型 vs 闭合类型上述代码中typeof(Container)是开放泛型类型。typeof(ContainerUser)是闭合泛型类型。结论特性是定义在源代码中的ContainerT类上的。无论你通过开放类型还是闭合类型去反射只要属性名匹配都能读到特性。通常建议使用typeof(YourClass)或者具体的typeof(YourClassSpecificType)均可。如果特性在泛型参数上如果你是指“获取T本身的特性”例如void Method[MyAttr] T()那是针对泛型参数的反射需要使用TypeInfo.GenericTypeParameters。但绝大多数场景如 ORM 映射、序列化配置特性都是加在属性声明上的如上面的[MyCustom] public ListT Items此时直接使用PropertyInfo.GetCustomAttribute即可。处理多个特性如果一个属性上有多个相同的特性使用GetCustomAttributesMyCustomAttribute()返回数组进行遍历。常见陷阱继承问题如果特性定义时没有设置Inherited true默认是 true且属性是在基类定义的在某些复杂的反射路径下可能需要注意bindingFlags是否包含DeclaredOnly。通常默认GetProperties()会包含继承链上的公共属性。接口代理如果你在使用 Entity Framework 或 Dynamic Proxy如 Castle.DynamicProxyobj.GetType()返回的可能是动态生成的代理类型。此时直接获取属性通常没问题但如果特性只定义在原始类上而不在接口上确保你反射的是正确的类型层级。总结代码片段如果你只需要一段核心代码来复用public static TAttribute GetGenericPropertyAttributeTAttribute, TClass, TProperty(string propertyName) where TAttribute : Attribute where TClass : class { // 获取类类型 (支持泛型类) Type classType typeof(TClass); // 获取属性信息 PropertyInfo propInfo classType.GetProperty(propertyName); if (propInfo null) throw new ArgumentException($Property {propertyName} not found on {classType.Name}); // 获取特性 return propInfo.GetCustomAttributeTAttribute(); } // 调用示例: // var attr GetGenericPropertyAttributeMyCustomAttribute, ContainerUser, ListUser(Items);这种方法完全适用于泛型属性因为反射系统在处理PropertyInfo时并不关心属性类型内部是否包含泛型参数它只关心属性元数据上挂载了什么特性。