C#中的BindingFlags:反射的核心枚举类型

在C#编程语言中,提供了一个非常强大的特性:反射(Reflection),让我们可以在运行时动态地获取和操作类型的信息。反射可以用来实现很多高级的功能,比如动态加载程序集,创建对象实例,调用方法,访问字段和属性等。

要使用反射,我们需要使用System.Reflection命名空间中的一些类,比如Type,Assembly,MethodInfo,FieldInfo,PropertyInfo等。这些类都提供了一些方法,可以让我们获取和操作类型的成员(Member),比如构造函数(Constructor),方法(Method),字段(Field),属性(Property),事件(Event)等。点个小关注吧👇

但是,要获取和操作类型的成员,我们不能随意地使用这些方法,而需要指定一个参数:BindingFlags。BindingFlags是一个枚举类型,它定义了一系列的Flag(标志),用来表示我们想要获取或操作哪些类型的成员。

BindingFlags枚举成员包括:

  • Default:表示使用默认的绑定规则,不指定任何特殊的标志。

  • IgnoreCase:表示忽略成员名称的大小写。

  • DeclaredOnly:表示只搜索在当前类型中声明的成员,不包括继承的成员。

  • Instance:表示搜索实例成员,即非静态的成员。

  • Static:表示搜索静态成员,即类级别的成员。

  • Public:表示搜索公共成员,即访问修饰符为public的成员。

  • NonPublic:表示搜索非公共成员,即访问修饰符为internal、protected或private的成员。

  • FlattenHierarchy:表示在搜索静态成员时,包括从基类继承的公共和受保护的静态成员,但不包括私有的静态成员和嵌套类型。

  • InvokeMethod:表示要调用一个方法,可以是构造函数、实例方法或静态方法。

  • CreateInstance:表示要创建一个类型的实例,调用与给定参数匹配的构造函数。

  • GetField:表示要获取一个字段的值,可以是实例字段或静态字段。

  • SetField:表示要设置一个字段的值,可以是实例字段或静态字段。

  • GetProperty:表示要获取一个属性的值,可以是实例属性或静态属性。

  • SetProperty:表示要设置一个属性的值,可以是实例属性或静态属性。

  • PutDispProperty:表示要调用一个COM对象上的PROPPUT成员,用于设置一个属性的值。

  • PutRefDispProperty:表示要调用一个COM对象上的PROPPUTREF成员,用于设置一个引用类型的属性的值。

  • ExactBinding:表示要求提供的参数类型必须与对应形参类型完全匹配,不允许进行类型转换。

  • SuppressChangeType:表示禁止进行类型转换,仅在COM互操作中使用。

  • OptionalParamBinding:表示返回参数数量与提供的参数数量匹配的成员集合,用于处理具有默认值或可变参数的方法。

  • IgnoreReturn:表示忽略方法的返回值,在COM互操作中使用。

  • DoNotWrapExceptions:表示不要将反射调用方法时产生的异常包装在TargetInvocationException中。

BindingFlags枚举成员归类分为:

  • 访问修饰符(Access Modifier):表示成员的可见性,有Public,NonPublic两个选项。

  • 成员类型(Member Type):表示成员的种类,有Constructor,Method,Field,Property,Event等多个选项。

  • 继承关系(Inheritance Relation):表示成员是否来自基类或接口,有DeclaredOnly一个选项。

  • 实例或静态(Instance or Static):表示成员是否属于实例或静态,有Instance,Static两个选项。

  • 调用方式(Invoke Method):表示调用方法时是否忽略大小写或抛出异常,有IgnoreCase,ThrowOnUnmappableChar等多个选项。

BindingFlags使用需注意以下几点:

  • BindingFlags是一个位域枚举,可以使用按位或运算符(|)组合多个枚举值,以指定多个绑定条件。

  • 在使用反射查找类型成员时,必须指定Instance或Static标志之一,以及Public或NonPublic标志之一,否则将返回空数组或null值。例如,如果只指定BindingFlags.Public,将无法找到任何成员。

  • 在使用反射调用成员时,必须指定InvokeMethod、CreateInstance、GetField、SetField、GetProperty或SetProperty标志之一,以表明要执行的操作。如果同时指定了多个操作标志,将抛出AmbiguousMatchException异常。

  • 在使用反射调用COM对象的成员时,必须使用PutDispProperty或PutRefDispProperty标志之一,以区分调用PROPPUT或PROPPUTREF成员。这些标志只适用于COM互操作场景,不应用于其他类型的对象。

  • 在使用反射调用具有默认值或可变参数的方法时,必须使用OptionalParamBinding标志,并且只能与InvokeMember方法结合使用。此外,还要注意参数的顺序和数量,以匹配正确的方法签名。

  • 在使用反射调用方法时,如果提供了自定义的Binder对象,则必须忽略ExactBinding和SuppressChangeType标志,因为这些标志与自定义绑定器的语义冲突。如果没有提供自定义绑定器,则默认绑定器会根据这些标志决定是否允许进行类型转换。

  • 在使用反射调用方法时,如果出现了异常,则反射会用TargetInvocationException包装这个异常。如果不想捕获这个包装异常,而是直接抛出原始异常,则可以使用DoNotWrapExceptions标志。

BindingFlags枚举支持其成员值的位运算组合,这意味着我们可以使用按位或运算符(|)来组合多个BindingFlags,以实现更精确的搜索条件。

例如:

/ 获取公共的实例方法
BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
// 获取非公共的静态字段
BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Static;
// 获取声明在当前类中的属性
BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly;

当我们使用反射中的一些方法时,我们需要传入一个BindingFlags参数来指定我们想要获取或操作哪些类型的成员。例如:​​​​​​​

/ 获取Person类中的所有公共实例属性
Type type = typeof(Person);
PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
// 调用Student类中名为SayHello的非公共静态方法
Type type = typeof(Student);
MethodInfo method = type.GetMethod("SayHello", BindingFlags.NonPublic | BindingFlags.Static);
method.Invoke(null, null);
// 设置Teacher类中名为Name的公共实例字段的值
Type type = typeof(Teacher);
FieldInfo field = type.GetField("Name", BindingFlags.Public | BindingFlags.Instance);
Teacher teacher = new Teacher();
field.SetValue(teacher, "Tom");

我们通一个例子来帮助理解,假设我们有一个类,如下所示:​​​​​​​


public class TestClass
{
    public int PublicField = 1;
    private int PrivateField = 2;
    protected int ProtectedField = 3;
    internal int InternalField = 4;
    protected internal int ProtectedInternalField = 5;
    private protected int PrivateProtectedField = 6;

    public void PublicMethod()
    {
        Console.WriteLine("PublicMethod");
    }

    private void PrivateMethod()
    {
        Console.WriteLine("PrivateMethod");
    }

    protected void ProtectedMethod()
    {
        Console.WriteLine("ProtectedMethod");
    }

    internal void InternalMethod()
    {
        Console.WriteLine("InternalMethod");
    }

    protected internal void ProtectedInternalMethod()
    {
        Console.WriteLine("ProtectedInternalMethod");
    }

    private protected void PrivateProtectedMethod()
    {
        Console.WriteLine("PrivateProtectedMethod");
    }
}

这个类有六个字段和六个方法,分别使用了不同的访问修饰符。现在,我们想要使用反射来获取这个类的所有字段和方法,并打印出它们的名称。我们可以使用以下代码来实现:​​​​​​​

Type type = typeof(TestClass);
Console.WriteLine("Fields:");
foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))
{
    Console.WriteLine(field.Name);
}
Console.WriteLine("Methods:");
foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))
{
    Console.WriteLine(method.Name);
}

我们使用了BindingFlags.Public、BindingFlags.NonPublic、BindingFlags.Instance和BindingFlags.Static四个Flag,以表示我们想要获取所有公共的、非公共的、实例的和静态的字段和方法。运行这段代码,我们可以得到以下输出:​​​​​​​

Fields:
PublicField
PrivateField
ProtectedField
InternalField
ProtectedInternalField
PrivateProtectedField
Methods:
PublicMethod
PrivateMethod
ProtectedMethod
InternalMethod
ProtectedInternalMethod
PrivateProtectedMethod
GetType
MemberwiseClone
Finalize
ToString
Equals
GetHashCode

可以看到,除了我们定义的六个字段和六个方法外,还有另外的六个方法也被返回了,它们分别是ToString、Equals、GetHashCode,MemberwiseClone,Finalize和GetType。这是因为这六个方法是从System.Object类继承而来的,而System.Object类是所有.NET类型的基类。由于我们使用了BindingFlags.FlattenHierarchy,所以反射会返回继承自基类的公共静态成员。如果我们不想要返回这些继承成员,我们可以使用BindingFlags.DeclaredOnly枚举值,以表示只返回在TestClass类中声明的成员。修改后的代码如下:​​​​​​​

Type type = typeof(TestClass);
Console.WriteLine("Fields:");
foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly))
{
    Console.WriteLine(field.Name);
}
Console.WriteLine("Methods:");
foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly))
{
    Console.WriteLine(method.Name);
}

运行这段代码,我们可以得到以下输出:​​​​​​​

Fields:
PublicField
PrivateField
ProtectedField
InternalField
ProtectedInternalField
PrivateProtectedField
Methods:
PublicMethod
PrivateMethod
ProtectedMethod
InternalMethod
ProtectedInternalMethod
PrivateProtectedMethod

这样就只返回了TestClass类中定义的字段和方法,不包括继承自基类的成员。

BindingFlags是一个非常重要的枚举类型,它可以让我们灵活地使用反射来获取和操作类的成员。通过了解BindingFlags的用法和注意事项,我们可以更好地利用反射这个强大的特性,实现更多的功能。

参考文档:​​​​​​​

https://learn.microsoft.com/en-us/dotnet/api/system.reflection.bindingflags?view=net-7.0
https://www.demo2s.com/csharp/csharp-bindingflags-tutorial-with-examples.html

 引入地址

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用反射来动态修改枚举类型字段的特性。下面是一个示例代码: ```csharp using System; using System.Reflection; public enum MyEnum { [MyAttribute("Value 1")] Value1, [MyAttribute("Value 2")] Value2, [MyAttribute("Value 3")] Value3 } public class MyAttribute : Attribute { public string Description { get; set; } public MyAttribute(string description) { Description = description; } } class Program { static void Main() { Type enumType = typeof(MyEnum); FieldInfo[] fields = enumType.GetFields(BindingFlags.Public | BindingFlags.Static); foreach (FieldInfo field in fields) { MyAttribute attribute = field.GetCustomAttribute<MyAttribute>(); if (attribute != null) { Console.WriteLine($"Original description for {field.Name}: {attribute.Description}"); // 修改特性的值 attribute.Description = "New description"; Console.WriteLine($"Modified description for {field.Name}: {attribute.Description}"); } } } } ``` 这段代码,我们定义了一个枚举类型 `MyEnum`,并为每个字段添加了一个自定义特性 `MyAttribute`。然后,我们使用反射获取枚举类型的字段,并使用 `GetCustomAttribute` 方法获取字段的特性。如果特性存在,我们可以通过修改特性的属性来动态修改特性的值。 在上述示例,我们将枚举类型 `MyEnum` 所有字段的特性 `Description` 修改为 "New description"。你可以根据自己的需要修改特性的其他属性。记得在实际使用要进行错误处理和适当的验证。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值