






















反射(Reflection)是.NET框架中一个强大的功能,允许程序在运行时动态地检查和操作类型、方法、属性等元数据。
在.NET中,程序集(Assembly)是代码和元数据的容器。程序集加载是反射的基础,CLR(公共语言运行时)通过Assembly类提供多种加载方式:
以下是一个简单的示例,展示如何使用Assembly.Load加载程序集并获取其类型:
using System;
using System.Reflection;
class Program
{
static void Main()
{
// 加载程序集
Assembly assembly = Assembly.Load("System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
// 获取程序集中的所有公共类型
Type[] types = assembly.GetExportedTypes();
foreach (Type type in types)
{
Console.WriteLine($"Type: {type.FullName}");
}
}
}
注意:使用ReflectionOnlyLoad时,需要注册回调方法以加载引用的程序集,因为CLR不会自动加载它们。
Mermaid图表:程序集加载流程
graph TD A[程序启动] --> B[调用Assembly.Load] B --> C[CLR查找TypeRef和AssemblyRef] C --> D{程序集是否已加载?} D -->|是| E[返回已加载的Assembly对象] D -->|否| F[加载程序集到AppDomain] F --> G[解析元数据] G --> H[返回Assembly对象]
反射允许开发者在运行时发现程序集中定义的类型。常用的方法包括:
Type对象。以下代码展示如何遍历程序集中的类型并列出其成员:
using System;
using System.Reflection;
class Program
{
static void Main()
{
// 加载当前程序集
Assembly assembly = Assembly.GetExecutingAssembly();
// 获取所有类型
foreach (Type type in assembly.GetTypes())
{
Console.WriteLine($"Type: {type.Name}");
foreach (MemberInfo member in type.GetTypeInfo().DeclaredMembers)
{
Console.WriteLine($" Member: {member.Name} ({member.MemberType})");
}
}
}
}
关键点:为了提高性能,建议使用接口或基类进行早期绑定,而不是频繁使用反射来访问成员。
反射不仅可以发现类型,还可以动态调用其成员(如方法、属性、构造函数)。以下是调用成员的主要方式:
示例代码展示如何动态调用方法和设置属性:
using System;
using System.Reflection;
class TestClass
{
public string Name { get; set; }
public void SayHello(string message) => Console.WriteLine($"Hello, {message}!");
}
class Program
{
static void Main()
{
// 获取类型
Type type = typeof(TestClass);
// 创建实例
object instance = Activator.CreateInstance(type);
// 设置属性
PropertyInfo prop = type.GetProperty("Name");
prop.SetValue(instance, "Grok");
// 调用方法
MethodInfo method = type.GetMethod("SayHello");
method.Invoke(instance, new object[] { "World" });
}
}
输出:
Hello, World!
性能优化:反射调用会带来性能开销,因为它涉及字符串搜索和参数打包。为提高性能,可以:
Delegate.CreateDelegate创建委托,缓存方法调用。RuntimeMethodHandle)减少内存占用。以下是使用委托优化方法调用的示例:
using System;
using System.Reflection;
class TestClass
{
public void SayHello(string message) => Console.WriteLine($"Hello, {message}!");
}
class Program
{
static void Main()
{
Type type = typeof(TestClass);
object instance = Activator.CreateInstance(type);
MethodInfo method = type.GetMethod("SayHello");
var sayHelloDelegate = (Action<string>)Delegate.CreateDelegate(typeof(Action<string>), instance, method);
sayHelloDelegate("World"); // 更高效的调用
}
}
反射在构建可扩展的应用程序(如插件系统)时非常有用。以下是设计插件系统的关键步骤:
HostSDK程序集,包含接口或基类。HostSDK,实现接口。以下是一个简单的插件系统示例:
using System;
using System.IO;
using System.Reflection;
public interface IPlugin
{
void Execute();
}
class Program
{
static void Main()
{
// 加载所有.dll文件
foreach (string file in Directory.GetFiles(".", "*.dll"))
{
Assembly assembly = Assembly.LoadFrom(file);
foreach (Type type in assembly.GetTypes())
{
if (typeof(IPlugin).IsAssignableFrom(type) && !type.IsInterface)
{
IPlugin plugin = (IPlugin)Activator.CreateInstance(type);
plugin.Execute();
}
}
}
}
}
建议:将接口定义在单独的程序集中,避免版本控制问题。考虑使用MEF(Managed Extensibility Framework)简化插件注册和发现。
反射虽然强大,但性能开销较大,主要原因包括:
优化策略:
Type、MethodInfo等对象。RuntimeTypeHandle)减少内存占用。以下是使用运行时句柄的示例:
using System;
using System.Reflection;
class Program
{
static void Main()
{
Type type = typeof(string);
RuntimeTypeHandle handle = Type.GetTypeHandle(type);
// 从句柄恢复Type对象
Type restoredType = Type.GetTypeFromHandle(handle);
Console.WriteLine($"Restored Type: {restoredType.FullName}");
}
}
以下是一些与反射和程序集加载相关的常见面试题:
Q1:什么是反射?在C#中有哪些实际应用场景?
解析:反射是运行时检查和操作类型元数据的机制。常见应用包括:
Q2:如何优化反射的性能?
解析:反射性能开销较大,可通过以下方式优化:
Type和MemberInfo对象。Delegate.CreateDelegate创建委托。RuntimeMethodHandle)减少内存占用。Q3:Assembly.Load和Assembly.LoadFrom有什么区别?
解析:
Assembly.Load:根据强名称加载程序集,优先从GAC(全局程序集缓存)查找,适合需要严格版本控制的场景。Assembly.LoadFrom:根据文件路径加载程序集,适合加载本地或动态下载的程序集。如果程序集已加载,返回现有实例。Q4:如何实现一个简单的插件系统?
解析:实现插件系统需要:
IPlugin)在独立的程序集中。Assembly.LoadFrom加载插件程序集,遍历类型,实例化实现IPlugin的类并调用其方法。此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。