C# 反射:Type 类与 Activator.CreateInstance()
在软件开发中,我们经常需要在运行时动态创建对象、调用方法或访问属性,而不是在编译时就确定具体类型。反射(Reflection)正是解决这一需求的利器。本文将深入探讨反射的核心——Type 类,以及如何使用 Activator.CreateInstance() 动态创建对象实例。
一、反射是什么?为什么需要它?
反射是 .NET 框架提供的一种强大机制,允许程序在运行时检查和操作类型信息。简单来说,反射就像一面镜子,让代码能够"看到"并"操控"自身的结构。
典型应用场景包括:
- 插件系统:主程序加载未知类型的插件,动态实例化并调用
- 依赖注入:框架根据配置在运行时创建对象
- 序列化/反序列化:根据类型信息动态创建对象实例
- ORM 框架:将数据库记录映射为对象实例
没有反射,这些场景都需要编写大量硬编码逻辑,难以扩展和维护。
二、Type 类:反射的入口点
System.Type 类是反射的核心,几乎所有反射操作都从这里开始。通过 Type 对象,你可以获取类的名称、方法、属性、构造函数等一切信息。
2.1 获取 Type 对象的四种方式
public class User
{
public string Name { get; set; }
public int Age { get; set; }
}
// 方式一:使用 typeof 运算符
Type type1 = typeof(User);
// 方式二:使用 GetType() 方法(实例调用)
User user = new User();
Type type2 = user.GetType();
// 方式三:使用 Type.GetType()(通过字符串)
Type type3 = Type.GetType("YourNamespace.User");
// 方式四:使用 Assembly 获取
Type type4 = Assembly.GetExecutingAssembly().GetType("User");
推荐:
typeof是最常用且性能最优的方式,因为它在编译时就能确定类型。
2.2 Type 类的常用成员
获取到 Type 对象后,你可以访问大量成员信息:
Type type = typeof(User);
// 获取类型名称
Console.WriteLine($"类型名: {type.Name}"); // User
Console.WriteLine($"完整名: {type.FullName}"); // YourNamespace.User
Console.WriteLine($"命名空间: {type.Namespace}"); // YourNamespace
// 判断类型基本特性
Console.WriteLine($"是类吗: {type.IsClass}"); // True
Console.WriteLine($"是接口吗: {type.IsInterface}"); // False
Console.WriteLine($"是public吗: {type.IsPublic}"); // True
// 获取所有公共属性
PropertyInfo[] properties = type.GetProperties();
foreach (var prop in properties)
{
Console.WriteLine($"属性: {prop.Name}, 类型: {prop.PropertyType.Name}");
}
// 获取所有公共方法
MethodInfo[] methods = type.GetMethods();
foreach (var method in methods)
{
Console.WriteLine($"方法: {method.Name}");
}
// 获取所有公共构造函数
ConstructorInfo[] constructors = type.GetConstructors();
foreach (var ctor in constructors)
{
Console.WriteLine($"构造函数: {ctor.Name}");
}
```
### 2.3 遍历字段和属性
```csharp
Type type = typeof(User);
User user = new User { Name = "张三", Age = 25 };
// 获取所有实例字段(包括私有)
FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
foreach (var field in fields)
{
Console.WriteLine($"字段: {field.Name}, 值: {field.GetValue(user)}");
}
// 获取所有属性(包括非公共的)
PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
foreach (var prop in properties)
{
Console.WriteLine($"属性: {prop.Name}, 值: {prop.GetValue(user)}");
}
```
---
## 三、Activator.CreateInstance():动态创建实例
`Activator.CreateInstance()` 是反射中最常用的方法之一,用于在运行时创建对象实例。它提供了多种重载形式,满足不同场景需求。
### 3.1 基本用法:无参构造函数
```csharp
// 方式一:使用 Type 对象
Type type = typeof(User);
User user1 = (User)Activator.CreateInstance(type);
// 方式二:泛型版本(推荐)
User user2 = Activator.CreateInstance<User>();
```
### 3.2 带参数构造函数
当类没有无参构造函数或需要传入参数时:
```csharp
public class User
{
public string Name { get; }
public int Age { get; }
public User(string name, int age)
{
Name = name;
Age = age;
}
}
// 创建带参数的对象
User user1 = (User)Activator.CreateInstance(typeof(User), "李四", 30);
// 泛型版本更清晰
User user2 = Activator.CreateInstance<User>("王五", 28);
```
### 3.3 CreateInstance 的完整重载签名
```csharp
// 1. 无参创建
object obj1 = Activator.CreateInstance(type);
// 2. 带参数创建(最多4个参数)
object obj2 = Activator.CreateInstance(type, "参数1", "参数2");
// 3. 指定绑定标志(控制访问级别)
object obj3 = Activator.CreateInstance(
type,
BindingFlags.Public | BindingFlags.NonPublic, // 搜索范围
null, // Binder
new object[] { "参数" }, // 构造函数参数
null // 区域设置
);
// 4. 通过数组传递参数(适用于多个参数)
object[] args = new object[] { "赵六", 35 };
User user = (User)Activator.CreateInstance(typeof(User), args);
```
---
## 四、实战案例
### 4.1 通用对象创建工厂
通过反射实现一个工厂模式,可以根据配置动态创建不同类型的对象:
```csharp
public interface IRepository
{
void Save();
}
public class UserRepository : IRepository
{
public void Save() => Console.WriteLine("保存用户");
}
public class ProductRepository : IRepository
{
public void Save() => Console.WriteLine("保存产品");
}
public static class RepositoryFactory
{
private static readonly Dictionary<string, Type> _registrations = new();
public static void Register<TInterface, TImplementation>()
where TImplementation : TInterface
{
string key = typeof(TInterface).Name;
_registrations[key] = typeof(TImplementation);
}
public static TInterface Create<TInterface>()
{
string key = typeof(TInterface).Name;
if (_registrations.TryGetValue(key, out Type implType))
{
return (TInterface)Activator.CreateInstance(implType)!;
}
throw new InvalidOperationException($"未注册 {key}");
}
}
// 使用
RepositoryFactory.Register<IRepository, UserRepository>();
RepositoryFactory.Register<IRepository, ProductRepository>();
IRepository repo1 = RepositoryFactory.Create<IRepository>();
repo1.Save(); // 输出: 保存用户
IRepository repo2 = RepositoryFactory.Create<IRepository>();
repo2.Save(); // 输出: 保存产品
4.2 动态方法调用
结合 MethodInfo 和 Activator 实现动态方法调用:
public class Calculator
{
public int Add(int a, int b) => a + b;
public string Greet(string name) => $"Hello, {name}";
}
public static class DynamicInvoker
{
public static object Invoke(string typeName, string methodName, params object[] args)
{
// 1. 获取类型
Type type = Type.GetType(typeName);
if (type == null)
throw new TypeLoadException($"无法加载类型: {typeName}");
// 2. 创建实例
object instance = Activator.CreateInstance(type);
// 3. 获取方法
MethodInfo method = type.GetMethod(methodName);
if (method == null)
throw new MissingMethodException(typeName, methodName);
// 4. 调用方法
return method.Invoke(instance, args);
}
}
// 使用
object result = DynamicInvoker.Invoke("Calculator", "Add", 10, 20);
Console.WriteLine(result); // 30
result = DynamicInvoker.Invoke("Calculator", "Greet", "World");
Console.WriteLine(result); // Hello, World
4.3 配置文件驱动的对象创建
实际项目中,经常需要根据配置文件动态创建对象:
{
"Logger": {
"Type": "ConsoleLogger, MyApp",
"Parameters": {
"Level": "Info"
}
}
}
public interface ILogger
{
void Log(string message);
}
public class ConsoleLogger : ILogger
{
private readonly string _level;
public ConsoleLogger(string level)
{
_level = level;
}
public void Log(string message)
{
Console.WriteLine($"[{_level}] {message}");
}
}
public static class ObjectFactory
{
public static T Create<T>(string typeName, Dictionary<string, object> parameters)
{
Type type = Type.GetType(typeName);
if (type == null)
throw new TypeLoadException($"类型加载失败: {typeName}");
// 收集构造函数参数
List<object> args = new();
ConstructorInfo[] ctors = type.GetConstructors();
if (ctors.Length > 0)
{
// 使用第一个构造函数(简化处理)
ParameterInfo[] paramInfos = ctors[0].GetParameters();
foreach (var param in paramInfos)
{
if (parameters.TryGetValue(param.Name!, out object value))
{
// 类型转换
object converted = Convert.ChangeType(value, param.ParameterType);
args.Add(converted);
}
}
}
return (T)Activator.CreateInstance(type, args.ToArray())!;
}
}
// 使用
var config = new Dictionary<string, object>
{
{ "Level", "Debug" }
};
ILogger logger = ObjectFactory.Create<ILogger>("ConsoleLogger, MyApp", config);
logger.Log("测试消息"); // [Debug] 测试消息
五、性能优化与注意事项
反射虽然强大,但相比直接调用,性能开销较大。以下是优化建议和注意事项:
| 策略 | 说明 |
|---|---|
| 缓存 Type 对象 | 避免重复调用 Type.GetType(),将 Type 对象缓存起来 |
| 使用泛型版本 | Activator.CreateInstance<T>() 比 (T)Activator.CreateInstance(type) 更高效 |
| 委托缓存 | 缓存 MethodInfo.Invoke 委托,避免重复查找 |
| 表达式树/IL Emit | 高频调用场景考虑使用 Expression Trees 或 IL Emit 生成动态代码 |
| 安全性检查 | 动态创建时注意检查类型是否可实例化(IsAbstract、IsInterface) |
5.1 安全检查实例
public static object SafeCreate(Type type)
{
// 安全检查
if (type == null)
throw new ArgumentNullException(nameof(type));
if (type.IsAbstract || type.IsInterface)
throw new ArgumentException($"类型 {type.Name} 是抽象类或接口,无法实例化");
// 查找无参构造函数
ConstructorInfo? ctor = type.GetConstructor(Type.EmptyTypes);
if (ctor == null)
throw new ArgumentException($"类型 {type.Name} 没有无参构造函数");
return Activator.CreateInstance(type)!;
}
5.2 委托缓存优化
对于高频调用的方法,使用委托缓存可以显著提升性能:
public static class MethodInvoker
{
private static readonly ConcurrentDictionary<MethodInfo, Func<object, object[], object>> _cache = new();
public static object Invoke(object instance, string methodName, params object[] args)
{
Type type = instance.GetType();
MethodInfo method = type.GetMethod(methodName, args.Select(a => a.GetType()).ToArray());
if (method == null)
throw new MissingMethodException(methodName);
// 从缓存获取或创建委托
if (!_cache.TryGetValue(method, out Func<object, object[], object>? invoker))
{
invoker = CreateInvoker(method);
_cache[method] = invoker;
}
return invoker(instance, args);
}
private static Func<object, object[], object> CreateInvoker(MethodInfo method)
{
// 使用 Expression Trees 创建高效委托
ParameterExpression instanceParam = Expression.Parameter(typeof(object), "instance");
ParameterExpression argsParam = Expression.Parameter(typeof(object[]), "args");
List<Expression> argsExpressions = new();
ParameterInfo[] parameters = method.GetParameters();
for (int i = 0; i < parameters.Length; i++)
{
Expression arg = Expression.ArrayIndex(argsParam, Expression.Constant(i));
argsExpressions.Add(Expression.Convert(arg, parameters[i].ParameterType));
}
Expression call = Expression.Call(
Expression.Convert(instanceParam, method.DeclaringType!),
method,
argsExpressions
);
Expression conversion = method.ReturnType == typeof(void)
? Expression.Block(call, Expression.Constant(null))
: Expression.Convert(call, typeof(object));
Func<object, object[], object> lambda = Expression.Lambda<Func<object, object[], object>>(
conversion,
instanceParam,
argsParam
).Compile();
return lambda;
}
}
六、总结
反射是 .NET 平台一项核心能力,通过 Type 类和 Activator.CreateInstance() 方法,我们可以在运行时动态探索和创建对象:
- Type 类 是反射的入口,提供了丰富的 API 来获取类型元数据
- Activator.CreateInstance() 支持多种方式创建对象实例,从无参到带参构造
- 实际应用中,合理使用反射可以实现灵活的插件架构、依赖注入和对象工厂
- 性能敏感场景需要配合缓存策略,或考虑替代方案如 Expression Trees
掌握反射技术,你将能够编写出更具扩展性和灵活性的应用程序。

暂无评论,快来抢沙发吧!