C# 中的反射与示例

C# 中的反射与示例

​ 在本文中,我将通过示例讨论C# 中的反射。C# 中的反射确定或检查程序集的内容。可以使用反射动态创建类型的实例、将类型绑定到现有对象,或者从现有对象获取类型并调用其方法或访问其字段和属性。在这篇文章中,将讨论什么是 C# 中的反射,如何实现反射,最后,我们将讨论何时在 C# 中使用反射。

一、C# 中的反射是什么?

​ 想要确定或检查程序集的内容时,需要进行反射。在这里,内容是指程序集的元数据,例如该程序集中的方法是什么、该程序集中的属性是什么、它们是公共的还是私有的等等。

​ 反射的最大实现之一是 Visual Studio 本身。假设,在 Visual Studio 中,我们创建了一个 String 类的对象,当我们按 obj. 然后 Visual Studio Intelligence 显示该对象的所有属性、方法、字段等,如下图所示。

https://dotnettutorials.net/lesson/reflection-in-csharp/

二、如何在C#中实现反射?

​ 实现反射是一个三步过程。步骤如下。

  1. 导入反射命名空间
  2. 获取对象的类型
  3. 浏览对象的元数据

​ 首先,我们需要导入 Reflection 命名空间,然后我们需要获取对象的类型,一旦我们获取了对象的类型,那么我们就可以去浏览元数据,即浏览方法、属性、变量等等。

​ 例如,使用反射显示 SomeClassLibrary 程序集的属性、方法和变量,如下实例代码所示:

using System;
//Step1: 导入 Reflection namespace
using System.Reflection;

namespace ReflectionDemo
{
    class Program
    {
        static void Main(string[] args)
        {            
            //Step2: 获取类型

            //获取程序集信息
            var MyAssembly = Assembly.LoadFile(@"D:\Projects\ReflectionDemo\SomeClassLibrary\bin\Debug\SomeClassLibrary.dll");

            //获取类信息
            var MyType = MyAssembly.GetType("SomeClassLibrary.Class1");

            //创建类型实例化对象
            dynamic MyObject = Activator.CreateInstance(MyType);

            //获取实例化对象
            Type parameterType = MyObject.GetType();

            //Step3: 获取相关元数据

            //获取所有public成员
            Console.WriteLine("All Public Fields");
            foreach (MemberInfo memberInfo in parameterType.GetFields())
            {
                Console.WriteLine(memberInfo.Name);
            }

            //获取所有public方法
            Console.WriteLine("\nAll Public Methods");
            foreach (MemberInfo memberInfo in parameterType.GetMethods())
            {
                Console.WriteLine(memberInfo.Name);
            }

            //获取所有属性
            Console.WriteLine("\nAll Public Properties");
            foreach (MemberInfo memberInfo in parameterType.GetProperties())
            {
                Console.WriteLine(memberInfo.Name);
            }

            Console.ReadKey();
        }
    }
}

测试输出结果:

在 C# 中浏览 SomeClassLibrary 程序集的属性、方法和变量

​ 在所有方法中,它还获取对象类方法。这是因为该对象是 .NET Framework 中所有类的超类。这里,get_P1和set_P1是公共属性P1的setter和getter方法。因此,可以通过以下方式使用 C# 中的反射来提取程序集的元数据。

using System;
using System.Reflection;

namespace ReflectionDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            //Get the Assembly Reference
            var MyAssembly = Assembly.LoadFile(@"D:\Projects\ReflectionDemo\SomeClassLibrary\bin\Debug\SomeClassLibrary.dll");

            //Get the Class Reference
            var MyType = MyAssembly.GetType("SomeClassLibrary.Class1");
            
            // Print the Type details
            Console.WriteLine($"Full Name = {MyType.FullName}");
            Console.WriteLine($"Just the Class Name = {MyType.Name}");
            Console.WriteLine($"Just the Namespace Name = {MyType.Namespace}");

            Console.ReadKey();
        }
    }
}

输出结果:

Reflection in C# with Examples

二、如何在C#中使用反射动态调用方法?

​ 反射的优点之一是它会检查程序集的元数据。使用反射的另一个好特性是可以使用反射调用 C# 中程序集的成员。因此,入伙上诉例子种要调用Method1。要使用反射调用程序集的方法,我们需要使用InvokeMember方法,如下图所示。

在 C# 中使用反射动态调用方法

**InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args):**此方法使用指定的绑定约束并匹配指定的参数列表来调用指定的成员。它返回一个表示被调用成员的返回值的对象。该方法采用以下参数:

  1. name:包含要调用的构造函数、方法、属性或字段成员的名称的字符串。在我们的例子中是方法1。
  2. invokeAttr:由一个或多个 System.Reflection.BindingFlags 组成的位掩码,指定如何进行搜索。访问权限可以是 BindingFlag 之一,例如 Public、NonPublic、Private、InvokeMethod、GetField 等。不需要指定查找类型。如果省略查找类型,则 BindingFlags.Public | BindingFlags.Instance | BindingFlags.Instance | BindingFlags.Instance 使用 BindingFlags.Static。
  3. binder:定义一组属性并启用绑定的对象,这可能涉及重载方法的选择、参数类型的强制以及通过反射调用成员。- 或 - 使用 System.Type.DefaultBinder 的空引用。请注意,要成功调用具有变量参数的方法重载,可能需要显式定义 System.Reflection.Binder 对象。在这里,传递一个空值。
  4. target:要在其上调用指定成员的对象。在示例中,对象是 MyObject。
  5. args:包含要传递给要调用的成员的参数的数组。由于方法不接受任何参数,因此我们在这里传递 null。

实例代码如下:

using System;
//Step1: 导入 Reflection namespace
using System.Reflection;

namespace ReflectionDemo
{
    class Program
    {
        static void Main(string[] args)
        {            
            //Step2: 获取类型

            //获取程序集信息
            var MyAssembly = Assembly.LoadFile(@"D:\Projects\ReflectionDemo\SomeClassLibrary\bin\Debug\SomeClassLibrary.dll");

            //获取类信息
            var MyType = MyAssembly.GetType("SomeClassLibrary.Class1");

            //创建类型实例化对象
            dynamic MyObject = Activator.CreateInstance(MyType);

            //获取实例化对象
            Type parameterType = MyObject.GetType();

            //Step3: 获取相关元数据

            //获取所有public成员
            Console.WriteLine("All Public Fields");
            foreach (MemberInfo memberInfo in parameterType.GetFields())
            {
                Console.WriteLine(memberInfo.Name);
            }

            //获取所有public方法
            Console.WriteLine("\nAll Public Methods");
            foreach (MemberInfo memberInfo in parameterType.GetMethods())
            {
                Console.WriteLine(memberInfo.Name);
            }

            //获取所有属性
            Console.WriteLine("\nAll Public Properties");
            foreach (MemberInfo memberInfo in parameterType.GetProperties())
            {
                Console.WriteLine(memberInfo.Name);
            }

            Console.ReadKey();
            Console.WriteLine("\nInvoking Method1");

            parameterType.InvokeMember("Method1",
                                        BindingFlags.Public | 
                                        BindingFlags.InvokeMethod | 
                                        BindingFlags.Instance,
                                        null, MyObject, null
                                      );
            
            Console.ReadKey();
        }
    }
}

输出结果:

在 C# 中使用反射动态调用方法的示例

四、总结

C#中Reflection的实时用途有哪些?
  1. 如果正在创建诸如 Visual Studio 编辑器之类的应用程序,您希望在其中显示内部详细信息,即使用 Intelligence 的对象的元数据。
  2. 在单元测试中有时我们需要调用私有方法来测试私有成员是否正常工作。
  3. 有时我们想转储属性、方法和程序集引用到文件或可能将其显示在屏幕上。
  4. 后期绑定也可以通过使用 C# 中的反射来实现。我们可以使用反射来动态创建类型的实例,而我们在编译时没有任何关于该类型的信息。因此,反射使我们能够使用编译时不可用的代码。
  5. 考虑一个例子,其中我们有一个接口的两个替代实现。您希望允许用户使用配置文件选择其中之一。通过反射,您可以简单地从配置文件中读取要使用其实现的类的名称,并实例化该类的实例。这是使用反射进行后期绑定的另一个示例。

能将其显示在屏幕上。
4. 后期绑定也可以通过使用 C# 中的反射来实现。我们可以使用反射来动态创建类型的实例,而我们在编译时没有任何关于该类型的信息。因此,反射使我们能够使用编译时不可用的代码。
5. 考虑一个例子,其中我们有一个接口的两个替代实现。您希望允许用户使用配置文件选择其中之一。通过反射,您可以简单地从配置文件中读取要使用其实现的类的名称,并实例化该类的实例。这是使用反射进行后期绑定的另一个示例。

**注意:**反射用于查找程序集中的所有类型和/或动态调用程序集中的方法。这包括有关对象的类型、属性、方法和事件的信息。通过反射,我们可以动态创建类型的实例,将类型绑定到现有对象,或者从现有对象获取类型并调用其方法或访问其字段和属性。因此,基本上使用反射,我们可以检查程序集的元数据,也可以在运行时调用方法。