C# 反射详解

发布于:2024-11-29 ⋅ 阅读:(22) ⋅ 点赞:(0)

反射是C#中的一个强大特性,允许程序在运行时检查和操作类型和对象的信息。

通过反射,你可以获取类型的属性方法构造函数等信息,并可以动态创建对象、调用方法或访问属性,甚至可以实现某些框架或库的核心功能。

反射的基本概念

  1. 类型信息:反射使我们能够获取类型的名称、成员(如属性、方法、字段)、基类、接口等信息。
  2. 动态创建对象:可以在运行时创建一个类的实例,而不需要在编译时知道具体的类。
  3. 访问成员:通过反射,可以访问类的私有成员,甚至是静态成员。

反射的常用类

反射的主要类都在 System.Reflection 命名空间中,以下是一些重要的类:

  • Type:表示类型的信息,可以用来获取类的信息,包括方法、属性、字段等。
  • Assembly:表示一个程序集,提供有关程序集的元数据。
  • MethodInfo:表示方法的信息,可以用来调用方法。
  • PropertyInfo:表示属性的信息,可以用来获取或设置属性的值。

反射常用类及其常用方法表

类名 常用方法 描述
Type GetProperties() 获取公共属性的数组。
GetMethods() 获取公共方法的数组。
GetFields() 获取公共字段的数组。
GetConstructors() 获取构造函数的信息。
GetInterfaces() 获取该类型实现在的所有接口。
GetCustomAttributes() 获取应用于当前类型的所有自定义特性(Attributes)。
BaseType 获取此类型的基类型(父类)。
PropertyInfo GetValue(object obj) 获取指定对象上属性的值。
SetValue(object obj, object value) 设置指定对象上属性的值。
CanRead 指示属性是否有获取访问器。
CanWrite 指示属性是否有设置访问器。
MethodInfo Invoke(object obj, object[] parameters) 调用方法。
GetParameters() 获取方法的参数信息。
ReturnType 获取方法的返回类型。
IsStatic 指示方法是否为静态方法。
FieldInfo GetValue(object obj) 获取指定对象上字段的值。
SetValue(object obj, object value) 设置指定对象上字段的值。
FieldType 获取字段的类型。
ConstructorInfo Invoke(object[] parameters) 调用构造函数以创建实例。
GetParameters() 获取构造函数的参数信息。
IsStatic 指示构造函数是否为静态构造函数。
Assembly GetTypes() 获取程序集中的所有类型。
GetName() 获取程序集的名称信息。
GetCustomAttributes() 获取应用于该程序集的所有自定义特性。

typeof 与 GetType

1. typeof 操作符

定义

  • typeof 是一个操作符,用于在编译时获取某个类型的 Type 对象。它适用于任何类型,包括类、结构、接口和基本类型。

用法

  • typeof 的语法是:Type type = typeof(SomeType);
  • 它返回的结果是 System.Type 的一个实例,表示指定类型的信息,能够用于反射。

2. GetType 方法

定义

  • GetType 是一个实例方法,它属于所有 .NET 对象的基类 Object。当你在一个对象实例上调用 GetType 方法时,它会返回该实例的 Type 对象。

用法

  • GetType 的语法是:Type type = someObject.GetType();
  • 它用于获取对象的实际类型信息。

特点 typeof GetType
类型获取方式 在编译时获取类型信息 在运行时获取对象实例的类型信息
使用场景 获取静态类型的 Type 对象 获取对象实例的 Type 对象
返回类型 总是返回特定类型的 Type 返回调用该方法的对象的实际类型的 Type
语法 Type type = typeof(ClassName); Type type = instance.GetType();

反射的使用示例

1. 获取类型信息

使用 Type 类可以获取对象的类型信息:

我们定义一个简单的类 Person,然后使用反射获取它的类型信息。

在此,我们将typeof和GetType进行对比。

using System;
namespace Tdm;
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    public void DisplayInfo()
    {
        Console.WriteLine($"姓名: {Name}, 年龄: {Age}");
    }
}

class Program
{
    static void Main()
    {
        Person person = new Person(); // 创建 Person 对象
        
        // 使用 GetType 方法
        Type typeFromInstance = person.GetType();
        Console.WriteLine("使用 GetType() 输出:");
        Console.WriteLine("类型名称: " + typeFromInstance.Name);       // 输出:类型名称: Person
        Console.WriteLine("命名空间: " + typeFromInstance.Namespace); // 输出:命名空间: (Tdm)

        // 使用 typeof 操作符
        Type typeFromTypeOf = typeof(Person); 
        Console.WriteLine("\n使用 typeof 输出:");
        Console.WriteLine("类型名称: " + typeFromTypeOf.Name);       // 输出:类型名称: Person
        Console.WriteLine("命名空间: " + typeFromTypeOf.Namespace); // 输出:命名空间: (Tdm)
    }
}
使用 GetType() 输出:
类型名称: Person
命名空间: (Tdm)

使用 typeof 输出:
类型名称: Person
命名空间: (Tdm)

2. 动态创建对象

我们使用反射动态创建 Person 对象,并设置其属性。

using System;

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    public void DisplayInfo()
    {
        Console.WriteLine($"姓名: {Name}, 年龄: {Age}");
    }
}

class Program
{
    static void Main()
    {
        Type personType = typeof(Person); // 获取 Person 类型
        Person person = (Person)Activator.CreateInstance(personType); // 动态创建 Person 实例

        // 设置属性值
        person.Name = "小明"; // 设置姓名
        person.Age = 25;      // 设置年龄

        person.DisplayInfo(); // 调用方法显示信息,输出:姓名: 小明, 年龄: 25
    }
}

3. 访问和修改属性

我们将使用反射访问和修改 Person 类的属性。

using System;
using System.Reflection;

public class Person
{
    public string Name { get; set; } // 姓名属性
    public int Age { get; set; }      // 年龄属性
}

class Program
{
    static void Main()
    {
        Person person = new Person(); // 创建 Person 对象
        Type type = person.GetType(); // 获取类型信息

        // 获取属性信息
        PropertyInfo nameProperty = type.GetProperty("Name"); // 获取 Name 属性
        PropertyInfo ageProperty = type.GetProperty("Age");   // 获取 Age 属性

        // 设置属性值
        nameProperty.SetValue(person, "张三"); // 通过反射设置 Name 属性
        ageProperty.SetValue(person, 30);      // 通过反射设置 Age 属性

        // 获取属性值并输出
        Console.WriteLine($"{nameProperty.Name}: {nameProperty.GetValue(person)}"); // 输出:Name: 张三
        Console.WriteLine($"{ageProperty.Name}: {ageProperty.GetValue(person)}");     // 输出:Age: 30
    }
}

4.调用方法

我们将使用反射调用 Person 类的方法。

using System;
using System.Reflection;

public class Person
{
    public void DisplayInfo(string greeting) // 显示信息的方法
    {
        Console.WriteLine($"{greeting}, 我是C#开发工程师!。"); // 输出问候信息
    }
}

class Program
{
    static void Main()
    {
        Person person = new Person(); // 创建 Person 对象
        Type type = person.GetType(); // 获取类型信息

        // 获取方法信息
        MethodInfo methodInfo = type.GetMethod("DisplayInfo"); // 获取 DisplayInfo 方法

        // 调用方法并传递参数
        methodInfo.Invoke(person, new object[] { "你好" }); // 输出:你好,我是C#开发工程师!
    }
}

反射的使用场景

1. 序列化和反序列化

反射非常适合于在对象和数据格式(如 JSON 或 XML)之间进行转换。使用反射,可以动态遍历对象的属性,将其值提取出来,构建相应的序列化格式。

示例:将一个对象转换为 JSON 字符串,或将一个 JSON 字符串转换为对象。

2. 依赖注入

许多现代框架(如 ASP.NET Core)使用反射来实现依赖注入。在运行时,框架可以通过反射创建对象实例和解析依赖关系。

示例:在 ASP.NET Core 中,使用反射自动识别控制器并注入其依赖服务。

3. 动态类型处理

在处理动态类型(例如,使用动态对象或 COM 组件)时,反射可以帮助获取类型信息并处理其方法和属性。

示例:通过反射操作一个未知类型的对象,尤其是在不确定其类型时。

注意事项

1. 性能问题

反射涉及到查找和动态调用,通常比直接调用方法慢。在性能敏感的代码中,应尽量减少反射的使用,或者对反射结果进行缓存。

2. 类型安全

反射不进行编译时类型检查,因此可能导致运行时错误。如果获取的成员不存在,程序将抛出异常。在使用反射时,务必保证类型的匹配。