文章目录

C# 反射:Type 类与 Activator.CreateInstance()

发布于 2026-04-05 13:34:43 · 浏览 12 次 · 评论 0 条

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 动态方法调用

结合 MethodInfoActivator 实现动态方法调用:

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 生成动态代码
安全性检查 动态创建时注意检查类型是否可实例化(IsAbstractIsInterface

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

掌握反射技术,你将能够编写出更具扩展性和灵活性的应用程序。

评论 (0)

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

扫一扫,手机查看

扫描上方二维码,在手机上查看本文