65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (66投票s)

2013年5月16日

CPOL

5分钟阅读

viewsIcon

183127

理解两个术语 - 反射和动态关键字

目录

引言

在本文中,我们将尝试理解两个术语:反射和 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();

第二步

一旦我们有了对象类型的引用,我们就可以调用 GetMembersGetProperties 来浏览类的所有方法和属性。

// 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 内部使用反射。它缓存已调用的方法调用,从而在一定程度上提高了性能。
  • 反射可以调用对象的 publicprivate 成员,而 dynamic 只能调用 public 成员。
  • dynamic 是实例特定的:你无法访问 static 成员;在这些场景下,你必须使用反射。

下面是一个详细的比较表,显示了它们适合的场景

  Reflection(反射) 动态
检查(元数据)
调用 public 成员
调用 private 成员
缓存
静态类

下面是一个简单的图表,直观地总结了反射和 dynamic 关键字的功能。

欲进一步阅读,请观看下面的面试准备视频和分步视频系列

历史

  • 2013年5月16日:初始版本
  • 2021年6月22日:更新
© . All rights reserved.