C#中的"动态"和"对象"关键字有什么区别



有人能简要解释一下C#中"dynamic"one_answers"object"关键字之间的区别吗?

对象

让我们先快速看一下object关键字。我不打算谈论太多,因为它从C#1.0开始就存在了。这个关键字不过是System的快捷方式。对象,它是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存储一个整数,但编译器不允许您在没有强制转换的情况下执行任何数学运算。它看起来可能会帮助您确保您确实有一个整数,但事实并非如此。您可以强制转换为完全不同的类型,但编译器不会检测到它。因此,您会得到运行时异常。

因此,您必须执行一个显式强制转换,它不能保证任何东西,因为编译器不允许您在没有强制转换的情况下运行程序。

动态

这是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;

这是对象和动态之间的主要区别之一——通过动态,您可以告诉编译器对象的类型只能在运行时知道,编译器不会试图干扰。因此,您可以编写更少的代码。我想强调的是,这并不比使用原始对象关键字更危险。然而,它也同样危险,因此在处理对象(如反射)时需要使用的所有类型检查技术都必须用于动态对象。

下一个经常出现的问题是这样的:"既然动态对象可以是任何东西,而编译器不检查它是什么,这是否意味着你可以将动态对象传递给我毫无戒心的方法/系统并使其崩溃?">

让我们假设我们有一个简单的方法。

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并不难,那么为什么要使用dynamic呢?好吧,有些情况下,您必须执行太多的强制转换操作,这会使您的代码几乎无法读取。还有一些情况下,简单的强制转换是不够的,您必须调用反射方法,如InvokeMember或GetProperties。这里的一个很好的例子是COM互操作,这就是为什么它被修改为使用新的动态特性(有关更多信息,请参阅此"操作方法"。)

此外,动态关键字和动态语言运行时支持许多以前不可能或难以实现的场景,包括与动态语言的互操作。我在本博客早些时候强调了两个这样的场景:介绍ExpandoObject和使用DynamicObject创建包装器。

结论

结论是,不必担心有人会通过使用动态特性破坏您的代码。它并不比object关键字更危险(而且同样危险)。

因此,如果您经常使用object关键字,并且必须执行大量强制转换和/或使用反射来调用对象的方法和属性,那么您可能应该查看dynamic关键字。在某些情况下,它比对象更方便,而且需要编写的代码更少。

请查看Anders Hejlsberg的这篇演讲,以更好地理解动态。

此外,请检查此页面上的C#关键字。

这些关键字指的是.NET常见类型。

基本上:对象类型是.NET Framework中对象的别名。在C#的统一类型系统中,所有类型,预定义类型和用户定义类型,引用类型和值类型(包括动态类型),都直接或间接继承Object。您可以将任何类型的值分配给对象类型的变量。

浅谈动态

动态类型使其发生的操作能够绕过编译时类型检查。相反,这些操作在运行时解决。动态类型简化了对COM API(如Office Automation API)、动态API(如IronPython库)和HTML文档对象模型(DOM)的访问。

在大多数情况下,类型动态的行为与类型对象类似。但是,编译器不会解析或检查包含dynamic类型表达式的操作。编译器将有关操作的信息打包在一起,这些信息稍后用于在运行时评估操作。作为过程的一部分,dynamic类型的变量被编译为object类型的变量。因此,类型dynamic只存在于编译时,而不存在于运行时。

请注意,此说明摘自MSDN页面。

  • object正是这样:一个只有最少方法的普通C#对象。在.NET中,它充当所有内容的内置基类(确保所有内容至少有ToString()Equals()GetHashCode())
  • dynamic对象则完全不同。dynamic允许在静态类型语言中对道具和方法进行后期绑定,从而带来一些功能,并与Python等动态类型语言具有更好的兼容性

最新更新