跳到主要内容

C# 反射基础

什么是反射?

反射(Reflection)是C#中一种强大的机制,允许程序在运行时动态获取类型信息、调用方法、访问属性或字段等。通过反射,你可以在不知道类型的情况下操作对象,这在某些场景下非常有用,例如插件系统、序列化、依赖注入等。

备注

反射的核心是 System.Reflection 命名空间,它提供了访问程序集、模块、类型和成员的类和方法。

反射的基本概念

1. 获取类型信息

在C#中,每个对象都有一个 Type 对象,它包含了该对象的类型信息。你可以通过 typeof 关键字或 GetType() 方法来获取类型信息。

csharp
using System;

class Program
{
static void Main()
{
// 使用 typeof 获取类型信息
Type type1 = typeof(string);
Console.WriteLine(type1.FullName); // 输出: System.String

// 使用 GetType() 获取类型信息
string str = "Hello";
Type type2 = str.GetType();
Console.WriteLine(type2.FullName); // 输出: System.String
}
}

2. 获取成员信息

通过反射,你可以获取类型的成员信息,例如方法、属性、字段等。以下示例展示了如何获取一个类的所有公共方法:

csharp
using System;
using System.Reflection;

class MyClass
{
public void Method1() { }
public void Method2() { }
}

class Program
{
static void Main()
{
Type type = typeof(MyClass);
MethodInfo[] methods = type.GetMethods();

foreach (var method in methods)
{
Console.WriteLine(method.Name);
}
}
}

输出:

Method1
Method2

3. 动态调用方法

反射不仅可以获取方法信息,还可以动态调用方法。以下示例展示了如何通过反射调用一个方法:

csharp
using System;
using System.Reflection;

class MyClass
{
public void SayHello(string name)
{
Console.WriteLine($"Hello, {name}!");
}
}

class Program
{
static void Main()
{
Type type = typeof(MyClass);
object instance = Activator.CreateInstance(type);

MethodInfo method = type.GetMethod("SayHello");
method.Invoke(instance, new object[] { "Alice" });
}
}

输出:

Hello, Alice!

4. 访问属性和字段

反射还可以用于访问对象的属性和字段。以下示例展示了如何获取和设置一个对象的属性值:

csharp
using System;
using System.Reflection;

class MyClass
{
public string Name { get; set; }
}

class Program
{
static void Main()
{
Type type = typeof(MyClass);
object instance = Activator.CreateInstance(type);

PropertyInfo property = type.GetProperty("Name");
property.SetValue(instance, "Bob");

string name = (string)property.GetValue(instance);
Console.WriteLine(name); // 输出: Bob
}
}

实际应用场景

1. 插件系统

反射常用于实现插件系统。通过反射,你可以在运行时加载外部程序集,并动态调用其中的方法或访问其属性。

csharp
using System;
using System.Reflection;

class Program
{
static void Main()
{
// 加载外部程序集
Assembly assembly = Assembly.LoadFrom("MyPlugin.dll");

// 获取类型
Type pluginType = assembly.GetType("MyPlugin.Plugin");

// 创建实例
object pluginInstance = Activator.CreateInstance(pluginType);

// 调用方法
MethodInfo method = pluginType.GetMethod("Run");
method.Invoke(pluginInstance, null);
}
}

2. 序列化与反序列化

反射在序列化和反序列化过程中也非常有用。通过反射,你可以动态获取对象的属性并将其转换为字符串,或者将字符串转换回对象。

csharp
using System;
using System.Reflection;

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

class Program
{
static void Main()
{
MyClass obj = new MyClass { Name = "Alice", Age = 30 };

// 序列化
Type type = obj.GetType();
PropertyInfo[] properties = type.GetProperties();
foreach (var property in properties)
{
Console.WriteLine($"{property.Name}: {property.GetValue(obj)}");
}

// 反序列化
MyClass newObj = new MyClass();
foreach (var property in properties)
{
property.SetValue(newObj, property.GetValue(obj));
}

Console.WriteLine(newObj.Name); // 输出: Alice
Console.WriteLine(newObj.Age); // 输出: 30
}
}

总结

反射是C#中一个非常强大的工具,它允许你在运行时动态获取和操作类型信息。通过反射,你可以实现插件系统、序列化、依赖注入等功能。然而,反射也有一些缺点,例如性能开销较大,因此在性能敏感的场景中应谨慎使用。

提示

反射虽然强大,但应尽量避免过度使用。在大多数情况下,静态类型检查是更好的选择。

附加资源与练习

  • 练习1:编写一个程序,使用反射获取一个类的所有公共属性,并输出它们的名称和值。
  • 练习2:实现一个简单的插件系统,允许用户通过命令行加载外部程序集并调用其中的方法。
警告

在使用反射时,请确保你了解其潜在的性能影响,并尽量避免在频繁调用的代码路径中使用反射。