C# 中反射和动态关键字有什么区别?






4.88/5 (66投票s)
理解两个术语 - 反射和动态关键字
目录
- 引言
- 什么是反射以及为什么我们需要它?
- 我们如何实现反射?
- 反射的实际用途有哪些?
- dynamic 关键字的用途是什么?
- dynamic 关键字的实际用途有哪些?
- 反射和 dynamic 之间的区别是什么?
- 历史
引言
在本文中,我们将尝试理解两个术语:反射和 dynamic
关键字。许多开发人员在这两者之间感到困惑,因为它们都可以帮助我们实现**动态调用**。
在本文中,我们将尝试揭示它们之间的区别,并了解我们在何种场景下使用哪一个。但在我们开始比较之前,让我们先分别理解它们,然后在文章的最后,我们将进行全面比较。
什么是反射以及为什么我们需要它?
当你想要**确定/检查程序集的 contents** 时,就需要反射。例如,看看你的 Visual Studio 编辑器的智能感知,当你输入任何对象的“.”(点)之前,它会显示该对象的所有成员。这是因为反射才得以实现。
反射还可以更进一步;它还可以调用已检查的成员。例如,如果反射检测到对象中有一个名为 GetChanges
的方法,我们可以获取该方法实例的引用并在运行时调用它。
简单来说,反射经过两个步骤:“**检查**”和“**调用**”(可选)。“**调用**”过程是可选的。
我们如何实现反射?
在 C# 中实现反射是一个两步过程,首先获取对象的“类型”,然后使用该类型来浏览“方法”、“属性”等成员。
步骤 1
第一步是获取对象的类型。因此,例如,你有一个 DLL ClassLibrary1.dll,其中有一个名为 Class1
的类。我们可以使用 Assembly
(属于 System.Reflection
命名空间)类来获取该对象类型的引用。之后,我们可以使用 Activator.CreateInstance
来创建该类的实例。GetType()
函数帮助我们获取该对象类型的引用。
var myAssembly = Assembly.LoadFile(@"C:\ClassLibrary1.dll");
var myType = myAssembly.GetType("ClassLibrary1.Class1");
dynamic objMyClass = Activator.CreateInstance(myType);
// Get the class type
Type parameterType = objMyClass.GetType();
第二步
一旦我们有了对象类型的引用,我们就可以调用 GetMembers
或 GetProperties
来浏览类的所有方法和属性。
// Browse through members
foreach (MemberInfo objMemberInfo in parameterType.GetMembers())
{Console.WriteLine(objMemberInfo.Name);}
// Browse through properties.
foreach (PropertyInfo objPropertyInfo in parameterType.GetProperties())
{Console.WriteLine(objPropertyInfo.Name);}
如果你想调用你已检查过的成员,可以使用 InvokeMember
来调用该方法。代码如下:
parameterType.InvokeMember("Display",BindingFlags.Public |
BindingFlags.NonPublic | BindingFlags.InvokeMethod |
BindingFlags.Instance,null, objMyClass, null);
反射的实际用途有哪些?
- 如果你正在创建一个像 Visual Studio 编辑器这样的应用程序,你希望通过智能感知显示对象内部信息。
- 如果你正在创建一个单元测试框架。在单元测试框架中,我们需要动态调用方法和属性以进行测试。
- 有时我们想将属性、方法和程序集引用转储(dump)到一个文件或在屏幕上显示。
dynamic 关键字的用途是什么?
编程语言可以分为两类:强类型和动态类型。强类型语言是指在编译时进行检查的语言,而动态类型语言是指在编译时绕过类型检查的语言。在动态类型语言中,对象的类型仅在运行时可知,并且类型检查仅在运行时激活。
我们希望兼顾这两种优点。因为很多时候,我们直到代码执行时才知道对象的类型。换句话说,我们正在寻找一种介于动态和静态类型之间的环境。这就是 dynamic
关键字的帮助所在。
如果你使用 dynamic
关键字创建了一个变量,并且尝试查看该对象的成员,你会收到如下消息:“将在运行时解析”。
现在尝试下面的代码。在代码中,我创建了一个 dynamic
变量,它被初始化为 string
数据。在第二行,我试图尝试执行一个数字增量操作。那么现在会发生什么?思考一下……
dynamic x = "c#";
x++;
现在这段代码将毫无疑问地编译通过。但在运行时,它会抛出一个异常,抱怨不能对变量执行数学运算,因为它是一个字符串类型。换句话说,在运行时,dynamic
对象从通用数据类型转换为特定数据类型(例如,对于下面的代码,是 string
)。
dynamic 关键字的实际用途有哪些?
dynamic
关键字最实际的用途之一是当我们通过互操作(interop)操作 MS Office 组件时。
所以,例如,如果我们访问 Microsoft Excel 组件而不使用 dynamic
关键字,你可以看到代码变得多么复杂。下面的代码进行了大量的类型转换,对吧?
// Before the introduction of dynamic.
Application excelApplication = new Application();
((Excel.Range)excelApp.Cells[1, 1]).Value2 = "Name";
Excel.Range range2008 = (Excel.Range)excelApp.Cells[1, 1];
现在看看使用 dynamic
关键字后代码变得多么简单。无需类型转换,并且在运行时也会进行类型检查。
// After the introduction of dynamic, the access to the Value property and
// the conversion to Excel.Range are handled by the run-time COM binder.
dynamic excelApp = new Application();
excelApp.Cells[1, 1].Value = "Name";
Excel.Range range2010 = excelApp.Cells[1, 1];
反射和 dynamic 之间的区别是什么?
- 当我们想在运行时操作对象时,反射和
dynamic
都被使用。 - 反射用于检查对象的元数据。它还能够在运行时调用对象的成员。
dynamic
是 .NET 4.0 中引入的一个关键字。它在运行时评估对象调用。因此,直到调用方法为止,编译器并不关心这些方法/属性是否存在。dynamic
内部使用反射。它缓存已调用的方法调用,从而在一定程度上提高了性能。- 反射可以调用对象的
public
和private
成员,而dynamic
只能调用public
成员。 dynamic
是实例特定的:你无法访问static
成员;在这些场景下,你必须使用反射。
下面是一个详细的比较表,显示了它们适合的场景
Reflection(反射) | 动态 | |
检查(元数据) | 是 | 否 |
调用 public 成员 | 是 | 是 |
调用 private 成员 | 是 | 否 |
缓存 | 否 | 是 |
静态类 | 是 | 否 |
下面是一个简单的图表,直观地总结了反射和 dynamic
关键字的功能。
欲进一步阅读,请观看下面的面试准备视频和分步视频系列
- C# 面试问答
- ASP.NET MVC 面试题及答案
- Angular 面试题及答案
- 逐步学习Azure。
- SQL Server 分步教程
- C# 抽象类与接口
- C# is vs As 关键字
- C# throw vs throw ex
- C# 并发 vs 并行
- C# 字符串是不可变的
历史
- 2013年5月16日:初始版本
- 2021年6月22日:更新