当前位置: 首页 > article >正文

C# BindingFlags 使用详解

总目录


前言

在 C# 编程的世界里,反射(Reflection)是一个强大且灵活的特性,它允许我们在运行时动态地获取和操作类型的信息。而 BindingFlags 枚举类型,作为反射中的核心概念之一,为我们提供了精确控制类型成员查找和操作的能力(通过组合多个标志来指定搜索类型 如字段、方法、属性等成员 的条件)。今天,就让我们深入探讨 BindingFlags 的使用,解锁反射的更多可能性。


一、什么是 BindingFlags?

1. 定义

BindingFlags 是一个枚举类型,它定义了一系列的标志,用于指定反射操作的搜索条件。它通过组合多个标志(Flags)来控制反射方法(如 GetFieldsGetMethodsGetProperty 等)的行为,使得我们可以灵活地控制要获取或操作的类型成员的范围和特性。

👉 在C#反射编程中,BindingFlags枚举类型是控制成员搜索与操作的核心导航器。

2. 关键特性

  • 类型System.Reflection.BindingFlags (位于 System.Reflection 命名空间)是一个枚举类型。
    • 位标志枚举 :BindingFlags 是一个 位标志枚举[Flags]),支持通过按位或(|)组合多个标志。
  • 作用:通过组合标志,控制反射方法(如 GetType().GetFields()GetType().GetMethods() 等)的搜索行为。
  • 强制指定:必须同时指定 InstanceStatic,以及 PublicNonPublic,否则会返回空结果。

二、常用 BindingFlags 标志详解

1. 常用标志总览

以下是 BindingFlags 中最常用的标志及其作用:

标志作用
Public包括公共成员(如 public 字段、方法、属性)。
NonPublic包括非公共成员(如 privateprotectedinternalprotected internal)。
Instance包括实例成员(非静态成员)。
Static包括静态成员(类级别的成员)。
DeclaredOnly仅搜索当前类型声明的成员,不包括继承的成员。
IgnoreCase忽略成员名称的大小写。
FlattenHierarchy在静态成员搜索中,包含基类的公共和受保护的静态成员(但不包括私有静态成员)。
CreateInstance通过反射创建对象实例(需与 InvokeMember 结合使用)。
InvokeMethod用于调用方法(需与 InvokeMember 结合使用)。
GetField/SetField用于获取或设置字段值(需与 InvokeMember 结合使用)。
GetProperty/SetProperty用于获取或设置属性值(需与 InvokeMember 结合使用)。

2. BindingFlags 的常用枚举值

1)IgnoreCase

当指定 IgnoreCase 时,反射在查找成员时会忽略大小写。这在处理不区分大小写的场景中非常有用。

using System;
using System.Reflection;

public class TestClass
{
    public void MyMethod()
    {
        Console.WriteLine("MyMethod called.");
    }
}

public class Program
{
    public static void Main()
    {
        Type type = typeof(TestClass);
        MethodInfo methodInfo = type.GetMethod("mymethod", BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public);
        if (methodInfo != null)
        {
            TestClass obj = new TestClass();
            methodInfo.Invoke(obj, null);
        }
    }
}

在这个示例中,即使方法名称是小写的 “mymethod”,通过 BindingFlags.IgnoreCase 也能成功找到并调用 MyMethod 方法。

2)DeclaredOnly

DeclaredOnly 标志用于仅查找在特定类型中声明的成员,而不包括从基类继承的成员。这有助于我们专注于当前类型自身定义的成员。

using System;
using System.Reflection;

public class BaseClass
{
    public void BaseMethod()
    {
        Console.WriteLine("BaseMethod called.");
    }
}

public class DerivedClass : BaseClass
{
    public void DerivedMethod()
    {
        Console.WriteLine("DerivedMethod called.");
    }
}

public class Program
{
    public static void Main()
    {
        Type type = typeof(DerivedClass);
        MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public);
        foreach (MethodInfo method in methods)
        {
            Console.WriteLine(method.Name);
        }
    }
}

运行结果将只输出 DerivedMethod,因为 DeclaredOnly 使得我们只查找在 DerivedClass 中声明的方法,而不包括从 BaseClass 继承的方法。

3)InstanceStatic

InstanceStatic 分别用于指定查找实例成员或静态成员。在反射操作中,必须明确指定要查找的成员类型。

using System;
using System.Reflection;

public class TestClass
{
    public static void StaticMethod()
    {
        Console.WriteLine("StaticMethod called.");
    }

    public void InstanceMethod()
    {
        Console.WriteLine("InstanceMethod called.");
    }
}

public class Program
{
    public static void Main()
    {
        Type type = typeof(TestClass);
        
        // 获取静态方法
        MethodInfo staticMethod = type.GetMethod("StaticMethod", BindingFlags.Static | BindingFlags.Public);
        staticMethod.Invoke(null, null);
        
        // 获取实例方法
        MethodInfo instanceMethod = type.GetMethod("InstanceMethod", BindingFlags.Instance | BindingFlags.Public);
        TestClass obj = new TestClass();
        instanceMethod.Invoke(obj, null);
    }
}

这里分别演示了如何获取和调用静态方法与实例方法,通过指定 BindingFlags.StaticBindingFlags.Instance 来明确查找范围。

4)PublicNonPublic

Public 用于查找公共成员,而 NonPublic 则用于查找非公共成员(包括内部、受保护和私有成员)。这使得我们能够访问类型中不同访问级别的成员。

using System;
using System.Reflection;

public class TestClass
{
    private string privateField;
    internal int internalProperty { get; set; }
    protected void ProtectedMethod()
    {
        Console.WriteLine("ProtectedMethod called.");
    }
}

public class Program
{
    public static void Main()
    {
        Type type = typeof(TestClass);
        TestClass obj = new TestClass();
        
        // 获取私有字段
        FieldInfo privateField = type.GetField("privateField", BindingFlags.NonPublic | BindingFlags.Instance);
        privateField.SetValue(obj, "Private field value");
        Console.WriteLine(privateField.GetValue(obj));
        
        // 获取内部属性
        PropertyInfo internalProperty = type.GetProperty("internalProperty", BindingFlags.NonPublic | BindingFlags.Instance);
        internalProperty.SetValue(obj, 42);
        Console.WriteLine(internalProperty.GetValue(obj));
        
        // 获取受保护方法
        MethodInfo protectedMethod = type.GetMethod("ProtectedMethod", BindingFlags.NonPublic | BindingFlags.Instance);
        protectedMethod.Invoke(obj, null);
    }
}

通过 BindingFlags.NonPublic,我们可以访问类中的私有字段、内部属性以及受保护的方法,这在某些需要深入操作类型内部状态的场景中非常有用。

5)FlattenHierarchy

FlattenHierarchy 标志用于在查找静态成员时,沿着类型层次结构向上搜索,包括基类中的公共和受保护静态成员。这对于需要获取整个继承链中的静态成员时非常方便。

using System;
using System.Reflection;

public class BaseClass
{
    public static void BaseStaticMethod()
    {
        Console.WriteLine("BaseStaticMethod called.");
    }
}

public class DerivedClass : BaseClass
{
    public static void DerivedStaticMethod()
    {
        Console.WriteLine("DerivedStaticMethod called.");
    }
}

public class Program
{
    public static void Main()
    {
        Type type = typeof(DerivedClass);
        MethodInfo[] methods = type.GetMethods(BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public);
        foreach (MethodInfo method in methods)
        {
            Console.WriteLine(method.Name);
        }
    }
}

运行结果将输出 BaseStaticMethodDerivedStaticMethod,因为 FlattenHierarchy 使得我们能够获取基类和派生类中的所有公共静态方法。

3. BindingFlags 的组合使用

BindingFlags 是一个位域枚举,这意味着我们可以使用按位或运算符(|)组合多个枚举值,以指定多个绑定条件。这为我们提供了极大的灵活性,能够满足各种复杂的反射需求。

using System;
using System.Reflection;

public class TestClass
{
    private int privateField;
    public void PublicMethod()
    {
        Console.WriteLine("PublicMethod called.");
    }
    private void PrivateMethod()
    {
        Console.WriteLine("PrivateMethod called.");
    }
}

public class Program
{
    public static void Main()
    {
        Type type = typeof(TestClass);
        TestClass obj = new TestClass();
        
        // 获取所有实例方法(公共和非公共)
        MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
        foreach (MethodInfo method in methods)
        {
            Console.WriteLine(method.Name);
        }
        
        // 获取私有字段
        FieldInfo privateField = type.GetField("privateField", BindingFlags.NonPublic | BindingFlags.Instance);
        privateField.SetValue(obj, 123);
        Console.WriteLine(privateField.GetValue(obj));
    }
}

在这个示例中,我们组合了 BindingFlags.InstanceBindingFlags.PublicBindingFlags.NonPublic 来获取类型中的所有实例方法,无论是公共的还是非公共的。同时,也演示了如何获取和操作私有字段。

4. BindingFlags基础配置规则

1)成员类型必选项

必须包含InstanceStatic标志中的一个(或同时包含):

// 获取所有公共实例成员(默认配置)
var publicMembers = type.GetMembers(BindingFlags.Public | BindingFlags.Instance);

// 获取私有静态字段
var privateStaticField = type.GetField("_cache", 
    BindingFlags.NonPublic | BindingFlags.Static);

原理:反射引擎需要明确区分实例成员与静态成员的搜索范围。

2)访问权限必选项

必须包含PublicNonPublic标志中的一个(或同时包含):

// 获取所有非公共方法(含私有和受保护)
var nonPublicMethods = type.GetMethods(
    BindingFlags.NonPublic | BindingFlags.Instance);

// 获取公共静态属性
var staticProperty = type.GetProperty("Logger",
    BindingFlags.Public | BindingFlags.Static);

注意:若未指定访问权限标志,默认仅搜索公共成员。

5. 典型组合示例

以下是一些典型场景的 BindingFlags 组合:

1)获取公共实例字段

Type type = typeof(MyClass);
// 公共 + 实例
BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance;

FieldInfo[] fields = type.GetFields(bindingFlags); 

2)获取私有静态方法

// 非公共 + 静态
BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Static;

MethodInfo method = typeof(MyClass).GetMethod("PrivateMethod", bindingFlags); 

3)获取所有实例属性(包括私有)

// 实例 + 公共 + 非公共
BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

PropertyInfo[] properties = typeof(MyClass).GetProperties(bindingFlags); 

4)获取当前类型声明的所有字段(不包括继承的)

// 仅当前类型声明的字段
BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public;

FieldInfo[] declaredFields = typeof(MyClass).GetFields(
    BindingFlags.DeclaredOnly | 
    BindingFlags.Instance | 
    BindingFlags.Public | 
    BindingFlags.NonPublic); 

5)忽略继承的成员

// 仅当前类型声明的私有实例字段
BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.NonPublic;

FieldInfo[] declaredFields = typeof(MyClass).GetFields(bindingFlags); 

6)忽略大小写搜索

// 忽略大小写获取字段 "MyField"
BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Public;

FieldInfo field = typeof(MyClass).GetField("myfield", bindingFlags);

三、实际应用场景

1. 访问不同条件下的成员

1)访问私有字段

public class MyClass
{
    private int _privateField;
}

// 通过反射获取私有字段
FieldInfo field = typeof(MyClass).GetField(
    "_privateField", 
    BindingFlags.NonPublic | BindingFlags.Instance);

// 设置字段值
var instance = new MyClass();
field.SetValue(instance, 42);

2)获取所有属性(包括私有)

public class MyClass
{
    public string PublicProp { get; set; }
    private int PrivateProp { get; set; }
}

// 获取所有属性(公共 + 私有)
PropertyInfo[] properties = typeof(MyClass).GetProperties(
    BindingFlags.Instance | 
    BindingFlags.Public | 
    BindingFlags.NonPublic);

3)忽略继承的成员

public class BaseClass
{
    public string BaseField;
}

public class DerivedClass : BaseClass
{
    public string DerivedField;
}

// 仅获取 DerivedClass 自身的字段
FieldInfo[] fields = typeof(DerivedClass).GetFields(
    BindingFlags.DeclaredOnly | 
    BindingFlags.Public | 
    BindingFlags.Instance);
// fields 包含 DerivedField,但不包含 BaseField

2. 动态调用复杂方法

1)调用私有方法

object instance = Activator.CreateInstance(type);
MethodInfo privateMethod = type.GetMethod("InternalProcess",BindingFlags.NonPublic | BindingFlags.Instance);
privateMethod.Invoke(instance, new object[] { data });

2)通过InvokeMember动态操作

// 设置私有静态字段值
type.InvokeMember("_instanceCount", 
    BindingFlags.SetField | BindingFlags.NonPublic | BindingFlags.Static,
    null, null, new object[] { 10 });

// 调用重载方法(指定参数类型)
object result = type.InvokeMember("Parse",
    BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static,
    null, null, new object[] { "2023", CultureInfo.CurrentCulture });

四、使用须知

1. 关键注意事项

1)必须指定实例Instance或静态Static标志

在使用反射查找类型成员时,必须指定 InstanceStatic 标志之一,否则将返回空数组或 null

// 错误:未指定 Instance 或 Static
var fields = type.GetFields(BindingFlags.Public); // 返回空数组

// 正确:同时指定 Public 和 Instance
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);

2)必须指定公共Public或非公共NonPublic标志

同样,需要指定 PublicNonPublic 标志之一来确定要查找的成员的访问级别

// 错误:未指定 Public 或 NonPublic
var fields = type.GetFields(BindingFlags.Instance); // 返回空数组

// 正确:同时指定 NonPublic 和 Instance
var fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);

3)默认绑定规则

如果不指定任何 BindingFlags,默认情况下将查找公共的实例和静态成员。

4)DeclaredOnly 的限制

  • 仅搜索当前类型声明的成员,不包括继承的成员。
  • 示例
    public class Base { public string BaseField; }
    public class Derived : Base { public string DerivedField; }
    
    // 仅获取 Derived 自身的字段
    var fields = typeof(Derived).GetFields(
        BindingFlags.DeclaredOnly | 
        BindingFlags.Public | 
        BindingFlags.Instance); // 只包含 DerivedField
    

5)FlattenHierarchy 的适用场景

  • 仅对静态成员有效,且不包含基类的私有静态成员。
  • 示例
    // 获取基类和当前类的公共静态方法
    var methods = typeof(Derived).GetMethods(
        BindingFlags.FlattenHierarchy | 
        BindingFlags.Public | 
        BindingFlags.Static);
    

6)IgnoreCase 的使用场景

  • 仅在需要忽略名称大小写时使用,例如处理用户输入的模糊匹配。

2. 性能优化技巧

// 缓存高频访问的MemberInfo对象
private static readonly MethodInfo _cachedMethod = 
    typeof(DataProcessor).GetMethod("Encrypt",
        BindingFlags.NonPublic | BindingFlags.Instance);

// 使用Expression Tree编译动态调用(比纯反射快10倍以上)
var instanceParam = Expression.Parameter(typeof(DataProcessor));
var callExpr = Expression.Call(instanceParam, _cachedMethod);
var lambda = Expression.Lambda<Action<DataProcessor>>(callExpr, instanceParam)
                      .Compile();
lambda.Invoke(processor);

3. 常见错误与解决方案

Q1:为什么返回空数组?

  • 原因:未正确组合 BindingFlags(如缺少 InstanceNonPublic)。
  • 解决:确保同时指定 Instance/StaticPublic/NonPublic

Q2:访问基类成员失败

  • 原因:使用了 DeclaredOnly 标志。
  • 解决:移除 DeclaredOnly 或接受仅当前类型的成员。

Q3:调用方法时参数类型不匹配?

  • 原因:未指定 ExactBinding 或参数类型不匹配。
  • 解决:确保参数类型与方法签名一致,或使用 ExactBinding 强制类型匹配。

4. 总结

  • BindingFlags 是反射的“导航仪”,通过组合标志精确控制搜索范围。
  • 关键规则
    1. 必须指定成员类型: InstanceStatic(或两者)。
    2. 必须指定访问权限: PublicNonPublic(或两者)。
    3. 使用 DeclaredOnly 时仅搜索当前类型声明的成员。
  • 典型组合
    • 公共实例成员:BindingFlags.Public | BindingFlags.Instance
    • 私有静态方法:BindingFlags.NonPublic | BindingFlags.Static
    • 所有实例字段:BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic

掌握BindingFlags的配置逻辑可显著提升反射代码的精准度与可靠性。


结语

回到目录页:C#/.NET 知识汇总
希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。


http://www.kler.cn/a/591815.html

相关文章:

  • 在linux 系统下的qt 安装mqtt库
  • maven在idea上搭建
  • flutter 专题 九十八 Flutter 1.7正式版发布
  • WPF 开发从入门到进阶(五)
  • JAVA EE(9)——线程安全——锁策略CAS
  • 【安全运营】用户与实体行为分析(UEBA)浅析
  • Lua语言的自动化测试
  • 【python】带有\n的json字符串,如何优雅打印
  • goweb中文件上传和文件下载
  • 监控视频联网平台在智慧水利中的应用
  • 技术革命、需求升级与商业生态迭代——基于开源AI大模型与智能商业范式的创新研究
  • 深入理解静态与动态代理设计模式:从理论到实践
  • Oracle常见系统函数
  • 华为供应链的变革模式和方法P105(105页PPT)(文末有下载方式)
  • 3D视觉相机引导机器人的原理
  • MongoDB下载安装
  • 深度学习框架PyTorch——从入门到精通(4)数据转换
  • vue/react前端项目打包的时候加上时间,防止后端扯皮
  • Python 视频爬取教程
  • 【SpringMVC】深入解析基于Spring MVC与AJAX的用户登录全流程——参数校验、Session管理、前后端交互与安全实践