一、概述和前景提要
由于上文已经详细介绍过如何利用Type类型获取类信息、属性信息、字段信息的相关知识点,不明白的小伙伴请移步上文:.NET进阶------深入理解反射(3)利用反射获取信息(对象、属性、字段)一、概述和前景提要 由于上文已经详细介绍过如何获取 - 掘金
本文分两个大点,分别是:
- 通过Type类型获取方法信息
- 通过Type类型获取特性信息
二、通过 Type 类型获取方法信息
MethodInfo 是反射中描述方法成员的核心类,继承自 MemberInfo,用于封装方法的元数据(如名称、参数、返回值、访问修饰符等),并支持动态调用方法。结合 Type 类的方法,可灵活获取任意类型的方法信息。
2.1 MethodInfo 核心属性与方法
关键属性(描述方法元数据)
| 属性 | 说明 |
|---|---|
Name |
获取方法名称(如 Show、Study)。 |
ReturnType |
获取方法的返回值类型(如 CustomContext、void)。 |
IsPublic/IsPrivate/IsProtected |
判断方法的访问修饰符。 |
IsStatic |
判断方法是否为静态方法。 |
IsInstance |
判断方法是否为实例方法(非静态)。 |
IsGenericMethod |
判断方法是否为泛型方法(如 T GetValue<T>())。 |
IsGenericMethodDefinition |
判断方法是否为泛型方法定义(未指定具体泛型参数,如 List<T>.Add(T))。 |
GetParameters() |
获取方法的参数列表,返回 ParameterInfo[](包含参数名称、类型、默认值等)。 |
DeclaringType |
获取声明该方法的类型(如方法所属的类)。 |
关键方法(操作方法)
| 方法 | 说明 |
|---|---|
Invoke(object obj, object[] parameters) |
动态调用方法:- 实例方法:obj 传入实例对象,parameters 传入参数数组(无参数传 null);- 静态方法:obj 传 null;- 返回值为方法的执行结果(void 方法返回 null)。 |
MakeGenericMethod(Type[] typeArgs) |
为泛型方法定义指定具体类型参数,返回可调用的泛型方法实例(如 GetDefault<int>())。 |
GetGenericArguments() |
获取泛型方法的类型参数(如 T),返回 Type[]。 |
GetCustomAttribute<T>(bool inherit) |
获取方法上的指定类型特性(继承自 MemberInfo)。 |
IsDefined(Type attributeType, bool inherit) |
检查方法是否应用了指定类型的特性(继承自 MemberInfo)。 |
GetParameters() |
获取方法的所有参数信息,包括参数名称、类型、是否为输出参数等。 |
2.2 实战示例:获取方法信息
示例 1:获取单个指定方法
支持获取公共 / 私有、静态 / 实例、带参数 / 无参数的方法,需通过 BindingFlags 筛选条件。
cs
// 定义测试类(含多种方法类型)
public class UserService
{
// 公共无参数实例方法
public string GetUserName() => "默认用户";
// 公共带参数实例方法
public int CalculateAge(int birthYear, int currentYear) => currentYear - birthYear;
// 私有静态方法
private static string GetPrivateMessage() => "私有静态方法的返回值";
// 泛型方法
public T GetDefaultValue<T>() => default(T);
}
Type serviceType = typeof(UserService);
UserService serviceInstance = new UserService();
// 1. 获取公共无参数方法 GetUserName
MethodInfo getUserNameMethod = serviceType.GetMethod("GetUserName");
Console.WriteLine($"方法名:{getUserNameMethod.Name}");
Console.WriteLine($"返回值类型:{getUserNameMethod.ReturnType.Name}");
// 动态调用方法(无参数)
string userName = (string)getUserNameMethod.Invoke(serviceInstance, null);
Console.WriteLine($"调用结果:{userName}\n");
// 2. 获取带参数方法 CalculateAge(需匹配参数类型)
Type[] paramTypes = new[] { typeof(int), typeof(int) };
MethodInfo calculateAgeMethod = serviceType.GetMethod("CalculateAge", paramTypes);
Console.WriteLine($"带参数方法:{calculateAgeMethod.Name}");
// 动态传入参数调用
int age = (int)calculateAgeMethod.Invoke(serviceInstance, new object[] { 2000, 2024 });
Console.WriteLine($"调用结果:{age}\n");
// 3. 获取私有静态方法 GetPrivateMessage(需指定 BindingFlags)
MethodInfo privateStaticMethod = serviceType.GetMethod(
"GetPrivateMessage",
BindingFlags.NonPublic | BindingFlags.Static
);
// 静态方法调用:obj参数传null
string privateMsg = (string)privateStaticMethod.Invoke(null, null);
Console.WriteLine($"私有静态方法调用结果:{privateMsg}\n");
// 4. 获取并调用泛型方法 GetDefaultValue<T>
MethodInfo genericMethodDef = serviceType.GetMethod("GetDefaultValue");
// 指定泛型参数为int,创建可调用的泛型方法实例
MethodInfo intDefaultMethod = genericMethodDef.MakeGenericMethod(typeof(int));
int intDefault = (int)intDefaultMethod.Invoke(serviceInstance, null);
Console.WriteLine($"int默认值:{intDefault}");
// 指定泛型参数为string
MethodInfo stringDefaultMethod = genericMethodDef.MakeGenericMethod(typeof(string));
string stringDefault = (string)stringDefaultMethod.Invoke(serviceInstance, null);
Console.WriteLine($"string默认值:{stringDefault ?? "null"}");
输出结果:
csharp
方法名:GetUserName
返回值类型:String
调用结果:默认用户
带参数方法:CalculateAge
调用结果:24
私有静态方法调用结果:私有静态方法的返回值
int默认值:0
string默认值:null
示例 2:获取多个方法(筛选条件)
通过 Type.GetMethods() 结合 BindingFlags,可批量获取符合条件的方法(如所有公共实例方法、所有静态方法等)。
cs
Type serviceType = typeof(UserService);
// 1. 获取所有公共实例方法(默认行为,含继承自object的方法)
MethodInfo[] publicInstanceMethods = serviceType.GetMethods();
Console.WriteLine("所有公共实例方法:");
foreach (var method in publicInstanceMethods)
{
// 过滤掉继承自object的方法(可选)
if (method.DeclaringType == serviceType)
{
string paramStr = string.Join(", ", method.GetParameters().Select(p => $"{p.ParameterType.Name} {p.Name}"));
Console.WriteLine($"- {method.ReturnType.Name} {method.Name}({paramStr})");
}
}
// 2. 获取所有静态方法(含公共+私有)
MethodInfo[] allStaticMethods = serviceType.GetMethods(
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static
);
Console.WriteLine("\n所有静态方法:");
foreach (var method in allStaticMethods)
{
string accessModifier = method.IsPublic ? "public" : "private";
Console.WriteLine($"- {accessModifier} static {method.ReturnType.Name} {method.Name}");
}
输出结果:
scss
所有公共实例方法:
- String GetUserName()
- Int32 CalculateAge(Int32 birthYear, Int32 currentYear)
- T GetDefaultValue<T>()
所有静态方法:
- private static String GetPrivateMessage()
示例 3:获取方法的特性信息
结合 MemberInfo 的特性相关方法,可获取方法上应用的自定义特性(如之前中间件示例中的 AbstractMiddleWareAttribute)。
cs
// 定义自定义特性(呼应之前的中间件场景)
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class LogAttribute : Attribute
{
public string Description { get; set; }
public bool NeedLog { get; set; } = true;
}
// 应用特性的测试方法
public class OrderService
{
[Log(Description = "创建订单", NeedLog = true)]
public void CreateOrder(string orderNo) { }
[Log(Description = "取消订单", NeedLog = false)]
[Log(Description = "记录取消操作", NeedLog = true)] // 允许多个特性
public void CancelOrder(string orderNo) { }
}
Type orderType = typeof(OrderService);
MethodInfo createOrderMethod = orderType.GetMethod("CreateOrder");
// 1. 检查方法是否应用了LogAttribute
bool hasLogAttr = createOrderMethod.IsDefined(typeof(LogAttribute), inherit: false);
Console.WriteLine($"CreateOrder方法是否有Log特性:{hasLogAttr}");
// 2. 获取单个LogAttribute(泛型方法,简洁)
LogAttribute logAttr = createOrderMethod.GetCustomAttribute<LogAttribute>();
if (logAttr != null)
{
Console.WriteLine($"特性描述:{logAttr.Description}");
Console.WriteLine($"是否需要日志:{logAttr.NeedLog}");
}
// 3. 获取多个特性(AllowMultiple = true时)
MethodInfo cancelOrderMethod = orderType.GetMethod("CancelOrder");
IEnumerable<LogAttribute> logAttrs = cancelOrderMethod.GetCustomAttributes<LogAttribute>();
Console.WriteLine("\nCancelOrder方法的所有Log特性:");
foreach (var attr in logAttrs)
{
Console.WriteLine($"- 描述:{attr.Description},是否日志:{attr.NeedLog}");
}
输出结果:
python
CreateOrder方法是否有Log特性:True
特性描述:创建订单
是否需要日志:True
CancelOrder方法的所有Log特性:
- 描述:取消订单,是否日志:False
- 描述:记录取消操作,是否日志:True
三、通过 Type 类型获取特性信息
特性(Attribute)是.NET 中用于为类型、成员(方法、属性、字段等)添加元数据的机制,反射可动态读取这些元数据,实现 "配置驱动" 或 "AOP" 等场景。获取特性的核心是 MemberInfo 类的相关方法(Type、MethodInfo、PropertyInfo 等均继承自 MemberInfo)。
3.1 特性获取的核心方法
| 方法 | 说明 |
|---|---|
GetCustomAttribute<T>(bool inherit) |
泛型版本(推荐):获取成员上指定类型的第一个 特性;- inherit = true:会查找继承自父类的特性;- 未找到返回 null。 |
GetCustomAttributes<T>(bool inherit) |
获取成员上指定类型的所有 特性,返回 IEnumerable<T>。 |
GetCustomAttributes(bool inherit) |
获取成员上的所有 特性,返回 object[](需强制类型转换)。 |
IsDefined(Type attributeType, bool inherit) |
检查成员是否应用了指定类型的特性,返回 bool(性能优于先获取再判断)。 |
3.2 实战示例:获取不同层级的特性
示例 1:获取类型上的特性
特性可直接应用于类、接口、枚举等类型,通过 Type 实例直接获取。
cs
// 定义应用于类型的特性
[AttributeUsage(AttributeTargets.Class)]
public class ModuleAttribute : Attribute
{
public string Name { get; set; }
public string Version { get; set; }
}
// 应用特性的类
[Module(Name = "用户模块", Version = "1.0.0")]
public class UserModule { }
Type moduleType = typeof(UserModule);
// 1. 检查类型是否有ModuleAttribute
bool hasModuleAttr = moduleType.IsDefined(typeof(ModuleAttribute), inherit: false);
Console.WriteLine($"UserModule是否有Module特性:{hasModuleAttr}");
// 2. 获取类型上的ModuleAttribute
ModuleAttribute moduleAttr = moduleType.GetCustomAttribute<ModuleAttribute>();
if (moduleAttr != null)
{
Console.WriteLine($"模块名称:{moduleAttr.Name}");
Console.WriteLine($"模块版本:{moduleAttr.Version}");
}
输出结果:
cs
UserModule是否有Module特性:True
模块名称:用户模块
模块版本:1.0.0
示例 2:获取属性上的特性
常用于 ORM 映射、API 验证等场景(如 EF Core 列名映射、Swagger 参数描述)。
cs
// 定义属性特性
[AttributeUsage(AttributeTargets.Property)]
public class ColumnAttribute : Attribute
{
public string Name { get; set; } // 数据库列名
public bool IsPrimaryKey { get; set; } // 是否为主键
}
// 应用特性的实体类
public class User
{
[Column(Name = "user_id", IsPrimaryKey = true)]
public int Id { get; set; }
[Column(Name = "user_name")]
public string Name { get; set; }
public int Age { get; set; } // 无特性
}
Type userType = typeof(User);
PropertyInfo[] properties = userType.GetProperties();
Console.WriteLine("User类属性的Column特性:");
foreach (var prop in properties)
{
ColumnAttribute columnAttr = prop.GetCustomAttribute<ColumnAttribute>();
if (columnAttr != null)
{
Console.WriteLine($"属性:{prop.Name}");
Console.WriteLine($"- 列名:{columnAttr.Name}");
Console.WriteLine($"- 是否主键:{columnAttr.IsPrimaryKey}\n");
}
else
{
Console.WriteLine($"属性:{prop.Name} → 无Column特性\n");
}
}
输出结果:
sql
User类属性的Column特性:
属性:Id
- 列名:user_id
- 是否主键:True
属性:Name
- 列名:user_name
- 是否主键:False
属性:Age → 无Column特性
示例 3:处理继承的特性
当特性设置 Inherited = true(默认)时,子类可继承父类的特性,通过 inherit = true 可获取。
cs
// 父类(应用特性)
[Module(Name = "基础模块", Version = "0.1.0")]
public class BaseModule { }
// 子类(不重新应用特性,继承父类)
public class OrderModule : BaseModule { }
Type orderModuleType = typeof(OrderModule);
// 1. inherit = true:获取继承的特性
ModuleAttribute inheritedAttr = orderModuleType.GetCustomAttribute<ModuleAttribute>(inherit: true);
Console.WriteLine("继承的特性:");
Console.WriteLine($"模块名称:{inheritedAttr.Name}");
Console.WriteLine($"模块版本:{inheritedAttr.Version}\n");
// 2. inherit = false:仅获取自身的特性(无)
ModuleAttribute selfAttr = orderModuleType.GetCustomAttribute<ModuleAttribute>(inherit: false);
Console.WriteLine($"自身是否有Module特性:{selfAttr != null}");
输出结果:
sql
继承的特性:
模块名称:基础模块
版本:0.1.0
自身是否有Module特性:False
3.3 特性获取的关键注意事项
AttributeUsage限制 :特性的应用范围(如仅类、仅方法)由AttributeTargets指定,超出范围的特性无法通过反射获取。AllowMultiple配置 :当特性设置AllowMultiple = true时,需用GetCustomAttributes获取所有特性,GetCustomAttribute仅返回第一个。- 性能考量 :反射获取特性的性能开销较低,但频繁调用时建议缓存结果(如缓存
PropertyInfo与特性的映射关系)。 - 继承特性的生效条件 :父类特性需设置
Inherited = true(默认值),子类才能通过inherit = true获取;接口的特性默认不被类继承。
三、总结
反射获取方法与特性信息是.NET 框架开发的核心技能之一,其核心价值在于动态性------ 无需编译时知道具体类型或成员,即可在运行时获取元数据并操作。