专业编程基础技术教程

网站首页 > 基础教程 正文

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

ccvgpt 2024-08-11 14:58:16 基础教程 10 ℃

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

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

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

但是,要获取和操作类型的成员,我们不能随意地使用这些方法,而需要指定一个参数: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标志之一,否则将返回空数组或值。例如,如果只指定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(, );// 设置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:PublicFieldPrivateFieldProtectedFieldInternalFieldProtectedInternalFieldPrivateProtectedFieldMethods:PublicMethodPrivateMethodProtectedMethodInternalMethodProtectedInternalMethodPrivateProtectedMethodGetTypeMemberwiseCloneFinalizeToStringEqualsGetHashCode

可以看到,除了我们定义的六个字段和六个方法外,还有另外的六个方法也被返回了,它们分别是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:PublicFieldPrivateFieldProtectedFieldInternalFieldProtectedInternalFieldPrivateProtectedFieldMethods:PublicMethodPrivateMethodProtectedMethodInternalMethodProtectedInternalMethodPrivateProtectedMethod

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

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

参考文档:

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


Tags:

最近发表
标签列表