object :
让我们先快速浏览一下 object 关键字。我不会谈论太多,因为它从 C# 1.0 就已经存在了。该关键字只不过是 System.Object 的快捷方式,System.Object 是 C# 类层次结构中的根类型。 (但是,正如 Eric Lippert 在他的博客文章中指出的那样,C# 中并非所有内容都派生自对象。)这是一种强大的机制,因为您可以为该类型的实例分配几乎任何值。
这是一个简短的示例,演示了使用 object 关键字的一些好处和问题。
object obj = 10;
Console.WriteLine(obj.GetType());
// Prints System.Int32 because
// this is the type of the value stored in this object.
// A compiler error, because
// at compile time the type of obj is System.Object.
// obj = obj + 10;
// You need to explicitly cast obj to a necessary type.
obj = (int)obj + 10;
// However, this does not mean that you are really safe.
// You can cast to a wrong type
// and the compiler will not detect it.
// Here you get an exception at run time,
// because obj is an integer, not a string.
// obj = (string)obj + 10;
// You also get a run-time exception
// if you cast to a wrong numeric type,
// even if there is an implicit conversion in the language.
// obj = (double)obj + 10;
正如您所看到的,虽然 obj 存储一个整数,但编译器不允许您在没有强制转换的情况下执行任何数学运算。看起来转换似乎可以帮助您确保确实拥有一个整数,但事实并非如此。您可以转换为完全不同的类型,编译器不会检测到它。结果,您会遇到运行时异常。
因此,您必须执行显式强制转换,这不能保证任何结果,因为编译器不允许您在没有强制转换的情况下运行程序。
dynamic :
这就是 C# 4.0 中新的动态关键字的用武之地。它告诉编译器不要对代码强制执行附加规则。
dynamic dyn = 10;
Console.WriteLine(dyn.GetType());
// Same as "object".
// Prints System.Int32 because
// this is the type of the value stored in this object.
// No compiler error, because
// the compiler does not try to identify
// the type of the dynamic object at compile time.
dyn = dyn + 10;
// Also, this operation will succeed for all numeric
// or other types that support a “+” operation.
dyn = 10.0;
dyn = dyn + 10;
dyn = "10";
dyn = dyn + 10;
这是对象和动态之间的主要区别之一 - 使用动态,您可以告诉编译器对象的类型只能在运行时知道,并且编译器不会尝试干扰。因此,您可以编写更少的代码。而且我想强调的是,这并不比使用原始 object 关键字更危险。然而,它也同样危险,因此操作对象时需要使用的所有类型检查技术(例如反射)也必须用于动态对象。
经常出现的下一个问题如下:“由于动态对象可以是任何东西,并且编译器不会检查它是什么,这是否意味着您可以将动态对象传递给我毫无戒心的方法/系统并制作它崩溃了吗?”
假设我们有一个简单的方法。
public static void Print(string arg)
{
Console.WriteLine(arg);
}
Now let’s look at how you can pass a dynamic object to it.
dynamic dyn = 10;
// You get an exception at run time here.
Print(dyn);
正如您所看到的,虽然编译器允许您将动态对象传递给您的方法,但如果该对象的类型错误,您的方法永远不会获取该对象。在实际调用方法之前抛出异常。将动态对象传递给方法的唯一方法是它是否包含必要的值(在本例中为字符串)。
dynamic dyn = "10";
Print(dyn);
同样,这与使用 object 关键字获得的行为没有太大区别。
object obj = 10;
// Doesn't compile.
//Print(obj);
// Compiles, but there is an exception at run time.
//Print((string)obj);
// This code works because obj is now a string,
// but you still need a cast.
obj = "10";
Print((string)obj);
有人说 (int)obj 读起来不难,何必费劲去搞动态呢?嗯,在某些情况下,您必须执行如此多的强制转换操作,这使得您的代码几乎不可读。在某些情况下,简单的强制转换还不够,您必须调用反射方法,例如 InvokeMember 或 GetProperties。 COM 互操作就是一个很好的例子,这就是为什么它被修改为使用新的动态功能(有关更多信息,请查看此“操作方法”。)
此外,dynamic 关键字和动态语言运行时使许多以前不可能或难以实现的场景成为可能,包括与动态语言的互操作。我之前在本博客中强调了几个这样的场景:介绍 ExpandoObject 和使用 DynamicObject 创建包装器。
结论 :
结论是,无需担心有人可以通过使用动态功能来破坏您的代码。它并不比 object 关键字更危险(同样也同样危险)。
因此,如果您经常使用 object 关键字并且必须执行大量转换和/或使用反射来调用对象的方法和属性,那么您可能应该看看dynamic 关键字。在某些情况下,它比对象更方便,并且需要编写的代码更少。