Activator.CreateInstance 与 Type.InvokeMember的区别

2024-06-08 05:44

本文主要是介绍Activator.CreateInstance 与 Type.InvokeMember的区别,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 一、使用 Activator.CreateInstance 创建实例
    • 1、使用 Activator.CreateInstance 的优点和缺点
    • 2、使用 Activator.CreateInstance 的代码示例
  • 二、使用 Type.InvokeMember 创建实例
    • 1、使用 Type.InvokeMember 的优点和缺点
    • 2、使用 Type.InvokeMember 的代码示例
  • 三、Activator.CreateInstance 与 Type.InvokeMember 的基准测试
    • 1、反射性能的 BenchmarkDotNet 设置
    • 2、Activator.CreateInstance 与 Type.InvokeMember对比
  • 四、Activator.CreateInstance 与 Type.InvokeMember 总结


一、使用 Activator.CreateInstance 创建实例

在 C# 中,Activator.CreateInstance方法是开发人员通过反射创建实例的最常用方法之一。它允许在运行时创建类的新实例,即使事先不知道它们的具体类型。此方法属于类,System.Activator通常用于需要动态实例化的场景。

的主要目的Activator.CreateInstance是动态创建类的实例 — 因此,如果您在编译时可以访问该类型,那么您可能没有充分的理由使用它!Activator.CreateInstance在要创建的对象类型直到运行时才知道的情况下,例如在加载插件或使用动态加载的程序集时,它特别有用。它消除了对硬编码显式构造函数的需求,并提供了对象创建的灵活性。

1、使用 Activator.CreateInstance 的优点和缺点

Activator.CreateInstance与普通对象实例相比,使用有几个优点:

允许后期绑定对象创建,这在对象类型在运行时可能发生变化的情况下很有用。
可以通过消除使用 switch 语句或 if-else 条件来处理不同对象类型的需求来简化代码库。
提供一种动态且可扩展的对象创建方法。
但是,使用时也需要考虑一些缺点Activator.CreateInstance:

由于运行时解析类型涉及额外的步骤,因此与直接实例化相比,使用反射的性能开销可能更高。
Activator.CreateInstance 通常依赖于公共无参数构造函数的存在。否则,您需要始终知道要传入哪些参数 — 如果您要针对许多不同类型动态执行此操作,则具有挑战性。
由于没有编译时检查签名兼容性,因此在修改目标类型时容易出现错误。

2、使用 Activator.CreateInstance 的代码示例

以下代码示例演示了如何使用Activator.CreateInstance动态创建实例:

// Example 1: Creating an instance of a known type
Type objectType = typeof(MyClass);
object instance = Activator.CreateInstance(objectType);

在示例 1 中,我们使用typeof获取Type代表已知类的对象MyClass。然后,我们使用Activator.CreateInstance创建 的新实例MyClass。

// Example 2: Creating an instance of an unknown type at runtime
string typeName = "MyNamespace.MyClass";
Type unknownType = Type.GetType(typeName);
object dynamicInstance = Activator.CreateInstance(unknownType);

在示例 2 中,我们有一个由字符串表示的未知类型typeName。我们使用根据提供的类型名称Type.GetType获取对象。最后,用于创建动态确定类型的新实例。TypeActivator.CreateInstance

我们再看一个例子,其中我们可以为构造函数传递参数——同样,假设我们知道签名,因为我们无法在编译时通过这种方法证明它:

// Example 3: Creating an instance with constructor parameters:
string typeName = "MyNamespace.MyClass";
Type unknownType = Type.GetType(typeName);
Object dynamicInstance = Activator.CreateInstance(unknownType,new[]{"Hello World!", //  this is the single string parameter!});

二、使用 Type.InvokeMember 创建实例

Type.InvokeMember是 DotNet 中 Reflection 提供的一种可用方法,允许我们动态创建类型的实例。它提供了一种灵活的方法,可以通过利用手头类型的信息在运行时实例化对象。出于这些原因,它在如何利用它来创建对象实例方面非常相似。

1、使用 Type.InvokeMember 的优点和缺点

Type.InvokeMember与普通对象实例相比,使用以下方法具有一些普遍的优点:

允许后期绑定对象创建,这在对象类型在运行时可能发生变化的情况下很有用。
可以通过消除使用 switch 语句或 if-else 条件来处理不同对象类型的需求来简化代码库。
提供一种动态且可扩展的对象创建方法。
等一下……这不就是我们上面看到的那个列表吗Activator.CreateInstance?没错。所以我们就简短地介绍一下这部分。除非我们开始研究性能,否则我们不会看到任何大的差异——也许在某些非常特殊的边缘情况下。但总的来说,两者都提供了非常全面的方法来动态实例化对象,而 InvokeMember 有点冗长,因为它处理的不仅仅是构造函数。

在进行基准测试之前,我们先检查一些代码。

2、使用 Type.InvokeMember 的代码示例

下面是一个示例代码片段,演示了如何Type.InvokeMember动态创建类型实例:

// Example 1: Creating an instance of a known type
Type objectType = typeof(MyClass);
var instance = objectType
.InvokeMember(null,BindingFlags.CreateInstance,null,null,null);

在上面的例子中,我们首先获取代表类“MyClass”的 Type 对象。然后我们使用 Type.InvokeMember 创建该类的实例并将其分配给“instance”变量。这使我们能够动态创建“MyClass”的对象,而无需明确指定类名。

而在编译时不知道类的情况下执行此操作与以前非常相似。但这部分与 InvokeMember 无关:

// Example 2: Creating an instance of an unknown type at runtime
string typeName = "MyNamespace.MyClass";
Type unknownType = Type.GetType(typeName);
var instance = objectType.InvokeMember(null,BindingFlags.CreateInstance,null,null,null);

最后,如果我们需要传递一些构造函数参数,那么我们也可以这样做:

// Example 3: Creating an instance with constructor parameters:
string typeName = "MyNamespace.MyClass";
Type unknownType = Type.GetType(typeName);
var instance = objectType.InvokeMember(null,BindingFlags.CreateInstance,null,null,new[]{"Hello World!",});

三、Activator.CreateInstance 与 Type.InvokeMember 的基准测试

1、反射性能的 BenchmarkDotNet 设置

我认为我们可以在三种情况下使用 BenchmarkDotNet 运行基准测试:

无参数构造函数类
具有单个字符串参数的构造函数
具有单个字符串参数的主构造函数
我想加入主构造函数,因为我知道这个特性会招致很多人的反对——最好还是获取一些数据吧!以下是我们将要实例化的类,供参考:

public class ParameterlessClass
{
}public class ClassicStringParameterClass
{private readonly string _value;public ClassicStringParameterClass(string value){_value = value;}
}public class PrimaryConstructorStringParameterClass(string _value)
{
}

至于基准测试,让我们看看我们将要运行的以下类。请记住,我使用它Activator.CreateInstance作为基线是因为我想比较Activator.CreateInstancevs Type.InvokeMember— 我只包括正常的构造函数路径作为参考。您也可以在 GitHub 上找到所有这些代码:

[ShortRunJob]
public class ParameterlessClassBenchmarks
{private Type? _type;[GlobalSetup]public void GlobalSetup(){_type = typeof(ParameterlessClass);}[Benchmark]public void Constructor(){var instance = new ParameterlessClass();}[Benchmark(Baseline = true)]public void Activator_Create_Instance(){var instance = Activator.CreateInstance(_type!);}[Benchmark]public void Type_Invoke_Member(){var instance = _type!.InvokeMember(null,BindingFlags.CreateInstance,null,null,null);}
}[ShortRunJob]
public class ClassicStringParameterClassBenchmarks
{private Type? _type;[GlobalSetup]public void GlobalSetup(){_type = typeof(ClassicStringParameterClass);}[Benchmark]public void Constructor(){var instance = new ClassicStringParameterClass("Hello World!");}[Benchmark(Baseline = true)]public void Activator_Create_Instance(){var instance = Activator.CreateInstance(_type!,new[]{"Hello World!",});}[Benchmark]public void Type_Invoke_Member(){var instance = _type!.InvokeMember(null,BindingFlags.CreateInstance,null,null,new[]{"Hello World!",});}
}[ShortRunJob]
public class PrimaryConstructorStringParameterClassBenchmarks
{private Type? _type;[GlobalSetup]public void GlobalSetup(){_type = typeof(PrimaryConstructorStringParameterClass);}[Benchmark]public void Constructor(){var instance = new PrimaryConstructorStringParameterClass("Hello World!");}[Benchmark(Baseline = true)]public void Activator_Create_Instance(){var instance = Activator.CreateInstance(_type!,new[]{"Hello World!",});}[Benchmark]public void Type_Invoke_Member(){var instance = _type!.InvokeMember(null,BindingFlags.CreateInstance,null,null,new[]{"Hello World!",});}
}

2、Activator.CreateInstance 与 Type.InvokeMember对比

当我们将这两种反射方法进行对比时,获胜者是…视情况而定。在最常见的一种情况下,我会说有一个非常明显的赢家,但对于其他情况,它们非常接近。但请务必阅读结论,因为这不是故事的结局。

我们要研究的第一个基准是无参数构造函数:
在这里插入图片描述

Activator.CreateInstance 与 Type.InvokeMember - 无参数构造函数的基准
这里明显胜出:Activator.CreateInstance几乎高出一个数量级。如果你的构造函数没有任何参数,那么最好的选择就是这个。

接下来,让我们检查Activator.CreateInstance一下Type.InvokeMember接受单个字符串参数的构造函数:
在这里插入图片描述

Activator.CreateInstance 与 Type.InvokeMember - 带参数的经典构造函数的基准
这两款车基本上势均力敌,尽管Activator.CreateInstance略胜一筹,但差距几乎可以忽略不计。

要查看的最后一个场景是主构造函数,在本例中,主构造函数接受一个字符串参数:
在这里插入图片描述

Activator.CreateInstance 与 Type.InvokeMember - 主构造函数的基准
获胜者:Type.InvokeMember,但只领先一点点。非常有趣的是,这与我们在之前的基准测试中看到的结果相反!

四、Activator.CreateInstance 与 Type.InvokeMember 总结

当谈到Activator.CreateInstancevs的性能结果时Type.InvokeMember,无参数构造函数的情况明显胜出:Activator.CreateInstance。但是当我们开始需要参数或使用带参数的主构造函数时,它开始均衡甚至有利于Type.InvokeMember。

这篇关于Activator.CreateInstance 与 Type.InvokeMember的区别的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1041343

相关文章

MySQL 筛选条件放 ON后 vs 放 WHERE 后的区别解析

《MySQL筛选条件放ON后vs放WHERE后的区别解析》文章解释了在MySQL中,将筛选条件放在ON和WHERE中的区别,文章通过几个场景说明了ON和WHERE的区别,并总结了ON用于关... 今天我们来讲讲数据库筛选条件放 ON 后和放 WHERE 后的区别。ON 决定如何 "连接" 表,WHERE

Mybatis的mapper文件中#和$的区别示例解析

《Mybatis的mapper文件中#和$的区别示例解析》MyBatis的mapper文件中,#{}和${}是两种参数占位符,核心差异在于参数解析方式、SQL注入风险、适用场景,以下从底层原理、使用场... 目录MyBATis 中 mapper 文件里 #{} 与 ${} 的核心区别一、核心区别对比表二、底

HTML5的input标签的`type`属性值详解和代码示例

《HTML5的input标签的`type`属性值详解和代码示例》HTML5的`input`标签提供了多种`type`属性值,用于创建不同类型的输入控件,满足用户输入的多样化需求,从文本输入、密码输入、... 目录一、引言二、文本类输入类型2.1 text2.2 password2.3 textarea(严格

Spring Boot Interceptor的原理、配置、顺序控制及与Filter的关键区别对比分析

《SpringBootInterceptor的原理、配置、顺序控制及与Filter的关键区别对比分析》本文主要介绍了SpringBoot中的拦截器(Interceptor)及其与过滤器(Filt... 目录前言一、核心功能二、拦截器的实现2.1 定义自定义拦截器2.2 注册拦截器三、多拦截器的执行顺序四、过

C# Semaphore与SemaphoreSlim区别小结

《C#Semaphore与SemaphoreSlim区别小结》本文主要介绍了C#Semaphore与SemaphoreSlim区别小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的... 目录一、核心区别概览二、详细对比说明1.跨进程支持2.异步支持(关键区别!)3.性能差异4.API 差

Java中自旋锁与CAS机制的深层关系与区别

《Java中自旋锁与CAS机制的深层关系与区别》CAS算法即比较并替换,是一种实现并发编程时常用到的算法,Java并发包中的很多类都使用了CAS算法,:本文主要介绍Java中自旋锁与CAS机制深层... 目录1. 引言2. 比较并交换 (Compare-and-Swap, CAS) 核心原理2.1 CAS

MySQL中VARCHAR和TEXT的区别小结

《MySQL中VARCHAR和TEXT的区别小结》MySQL中VARCHAR和TEXT用于存储字符串,VARCHAR可变长度存储在行内,适合短文本;TEXT存储在溢出页,适合大文本,下面就来具体的了解... 目录一、VARCHAR 和 TEXT 基本介绍1. VARCHAR2. TEXT二、VARCHAR

python中getsizeof和asizeof的区别小结

《python中getsizeof和asizeof的区别小结》本文详细的介绍了getsizeof和asizeof的区别,这两个函数都用于获取对象的内存占用大小,它们来自不同的库,下面就来详细的介绍一下... 目录sys.getsizeof (python 内置)pympler.asizeof.asizeof

Vue和React受控组件的区别小结

《Vue和React受控组件的区别小结》本文主要介绍了Vue和React受控组件的区别小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录背景React 的实现vue3 的实现写法一:直接修改事件参数写法二:通过ref引用 DOMVu

Go之errors.New和fmt.Errorf 的区别小结

《Go之errors.New和fmt.Errorf的区别小结》本文主要介绍了Go之errors.New和fmt.Errorf的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考... 目录error的基本用法1. 获取错误信息2. 在条件判断中使用基本区别1.函数签名2.使用场景详细对