深入理解 C# 反射:基础原理与实际应用

反射(Reflection)是 .NET 中一项强大的功能,它允许程序在运行时动态获取类型信息、创建对象、调用方法、以及访问和修改类的成员(字段、属性等)。反射提供了极大的灵活性和可扩展性,特别适用于那些需要在运行时动态操作类型的场景。然而,反射也有性能开销,因此在使用时需要谨慎。本文将详细介绍 C# 反射的基本用法,包括如何获取类型信息、动态创建对象、动态调用方法、访问和修改字段/属性的值,以及获取程序集和模块信息。

1. 获取类型信息(字段、属性、方法等)

反射允许你在运行时获取对象的类型信息,进而访问类的字段、属性、方法等成员。你可以通过 Type 类来获取类型信息,利用该信息进行动态操作。

示例:获取字段、属性和方法信息
复制代码
using System;
using System.Reflection;

public class MyClass
{
    public int Field1 = 10;
    private string Field2 = "Hello";

    public int MyProperty { get; set; }

    public void MyMethod()
    {
        Console.WriteLine("Method executed!");
    }

    private void PrivateMethod()
    {
        Console.WriteLine("Private method executed!");
    }
}

class Program
{
    static void Main()
    {
        MyClass obj = new MyClass();
        Type type = obj.GetType(); // 获取类型信息

        // 获取字段信息
        FieldInfo field1 = type.GetField("Field1");
        Console.WriteLine("Field1: " + field1.GetValue(obj));  // 输出:Field1: 10

        // 获取私有字段信息
        FieldInfo field2 = type.GetField("Field2", BindingFlags.NonPublic | BindingFlags.Instance);
        Console.WriteLine("Field2: " + field2.GetValue(obj));  // 输出:Field2: Hello

        // 获取属性信息
        PropertyInfo property = type.GetProperty("MyProperty");
        Console.WriteLine("MyProperty: " + property.GetValue(obj));  // 输出:MyProperty: 0 (默认值)

        // 获取方法信息
        MethodInfo method = type.GetMethod("MyMethod");
        method.Invoke(obj, null);  // 输出:Method executed!

        // 获取私有方法信息
        MethodInfo privateMethod = type.GetMethod("PrivateMethod", BindingFlags.NonPublic | BindingFlags.Instance);
        privateMethod.Invoke(obj, null);  // 输出:Private method executed!
    }
}

在上面的代码中,Type.GetType() 用来获取类的类型信息,GetField(), GetProperty(), GetMethod() 等方法可以帮助你获取字段、属性和方法的信息。通过 BindingFlags.NonPublic 可以访问私有成员。

2. 动态创建对象

反射使得在运行时动态创建对象成为可能。你可以通过类型信息来创建对象,甚至是使用带参数的构造函数来创建对象。

示例:动态创建对象并调用方法
复制代码
using System;

public class MyClass
{
    public MyClass() { Console.WriteLine("Constructor called!"); }
    public void DisplayMessage() { Console.WriteLine("Hello from MyClass!"); }
}

class Program
{
    static void Main()
    {
        Type type = typeof(MyClass);

        // 动态创建对象
        object obj = Activator.CreateInstance(type);

        // 调用方法
        MethodInfo method = type.GetMethod("DisplayMessage");
        method.Invoke(obj, null);  // 输出:Hello from MyClass!
    }
}

在这个示例中,Activator.CreateInstance() 方法用来动态创建 MyClass 类的实例,之后通过反射调用了 DisplayMessage 方法。

3. 动态调用方法

通过反射,你可以在运行时动态调用对象的公共或私有方法。即使方法的名称、参数等信息在编译时未知,也可以通过反射来动态调用。

示例:动态调用公共和私有方法
复制代码
using System;
using System.Reflection;

public class MyClass
{
    private void PrivateMethod()
    {
        Console.WriteLine("Private method called!");
    }

    public void PublicMethod()
    {
        Console.WriteLine("Public method called!");
    }
}

class Program
{
    static void Main()
    {
        MyClass obj = new MyClass();
        Type type = obj.GetType();

        // 调用公共方法
        MethodInfo publicMethod = type.GetMethod("PublicMethod");
        publicMethod.Invoke(obj, null);  // 输出:Public method called!

        // 调用私有方法
        MethodInfo privateMethod = type.GetMethod("PrivateMethod", BindingFlags.NonPublic | BindingFlags.Instance);
        privateMethod.Invoke(obj, null);  // 输出:Private method called!
    }
}

在上面的代码中,我们使用 MethodInfo.Invoke() 方法动态调用了公共方法 PublicMethod 和私有方法 PrivateMethod

4. 访问和修改字段、属性的值

反射不仅能够读取对象的字段和属性,还能修改它们的值。你可以通过反射动态地访问和修改字段或属性,无论它们是公共的还是私有的。

示例:访问和修改字段、属性的值
复制代码
using System;
using System.Reflection;

public class MyClass
{
    public int PublicField = 100;
    private string PrivateField = "Hello";

    public int MyProperty { get; set; }
}

class Program
{
    static void Main()
    {
        MyClass obj = new MyClass();
        Type type = obj.GetType();

        // 获取和修改字段值
        FieldInfo publicField = type.GetField("PublicField");
        Console.WriteLine("PublicField: " + publicField.GetValue(obj));  // 输出:PublicField: 100
        publicField.SetValue(obj, 200);
        Console.WriteLine("PublicField (after modification): " + publicField.GetValue(obj));  // 输出:PublicField (after modification): 200

        // 获取私有字段值
        FieldInfo privateField = type.GetField("PrivateField", BindingFlags.NonPublic | BindingFlags.Instance);
        Console.WriteLine("PrivateField: " + privateField.GetValue(obj));  // 输出:PrivateField: Hello
        privateField.SetValue(obj, "New Value");
        Console.WriteLine("PrivateField (after modification): " + privateField.GetValue(obj));  // 输出:PrivateField (after modification): New Value

        // 获取和修改属性值
        PropertyInfo property = type.GetProperty("MyProperty");
        property.SetValue(obj, 50);
        Console.WriteLine("MyProperty: " + property.GetValue(obj));  // 输出:MyProperty: 50
    }
}

在这个示例中,FieldInfo.GetValue()PropertyInfo.GetValue() 方法用于读取字段或属性的值,FieldInfo.SetValue()PropertyInfo.SetValue() 方法则用于修改字段或属性的值。

5. 获取程序集和模块信息

反射还可以用于获取当前程序集中所有的类型、方法、属性等信息。程序集(Assembly)是包含类型定义的容器,而模块(Module)是程序集中的组成部分。通过反射,可以获取程序集和模块的信息。

示例:获取程序集和模块信息
复制代码
using System;
using System.Reflection;

class Program
{
    static void Main()
    {
        // 获取当前程序集
        Assembly assembly = Assembly.GetExecutingAssembly();
        Console.WriteLine("Assembly: " + assembly.FullName);

        // 获取程序集中的所有类型
        Console.WriteLine("\nTypes in Assembly:");
        foreach (Type type in assembly.GetTypes())
        {
            Console.WriteLine(type.FullName);
        }

        // 获取模块信息
        Console.WriteLine("\nModules in Assembly:");
        foreach (Module module in assembly.GetModules())
        {
            Console.WriteLine("Module: " + module.Name);
        }
    }
}

在这个示例中,使用 Assembly.GetExecutingAssembly() 获取当前程序的程序集信息,通过 GetTypes() 方法列出程序集中的所有类型,GetModules() 获取程序集中的模块信息。

6. 总结

反射是 C# 中非常强大且灵活的功能,它允许程序在运行时动态获取类型信息、创建对象、调用方法、访问和修改字段及属性的值。常见的反射用途包括:

  1. 获取类型信息 :通过 Type 获取类的字段、属性、方法等信息。
  2. 动态创建对象 :使用 Activator.CreateInstance() 动态创建对象。
  3. 动态调用方法 :通过 MethodInfo.Invoke() 动态调用对象的方法。
  4. 访问和修改字段/属性:使用反射读取和修改对象的字段或属性值。
  5. 获取程序集和模块信息:通过反射获取程序集和模块的详细信息。

尽管反射提供了极大的灵活性,但由于其性能开销较大,因此应谨慎使用,尤其是在性能要求较高的场景中。反射通常用于需要高度灵活性和可扩展性的场景,例如插件框架、动态代理和序列化等。

相关推荐
上单带刀不带妹29 分钟前
手写 Vue 中虚拟 DOM 到真实 DOM 的完整过程
开发语言·前端·javascript·vue.js·前端框架
-凌凌漆-1 小时前
【Qt】QStringLiteral 介绍
开发语言·qt
程序员爱钓鱼1 小时前
Go语言项目工程化 — 常见开发工具与 CI/CD 支持
开发语言·后端·golang·gin
军训猫猫头2 小时前
1.如何对多个控件进行高效的绑定 C#例子 WPF例子
开发语言·算法·c#·.net
真的想上岸啊2 小时前
学习C++、QT---18(C++ 记事本项目的stylesheet)
开发语言·c++·学习
明天好,会的2 小时前
跨平台ZeroMQ:在Rust中使用zmq库的完整指南
开发语言·后端·rust
丁劲犇3 小时前
用 Turbo Vision 2 为 Qt 6 控制台应用创建 TUI 字符 MainFrame
开发语言·c++·qt·tui·字符界面·curse
旷世奇才李先生3 小时前
Next.js 安装使用教程
开发语言·javascript·ecmascript
charlie1145141914 小时前
深入理解Qt的SetWindowsFlags函数
开发语言·c++·qt·原理分析
likeGhee4 小时前
python缓存装饰器实现方案
开发语言·python·缓存