43

c# 3.0 中的匿名类型(var)和 c# 4.0 中的动态类型(动态)之间的真正区别是什么?

4

5 回答 5

102

您似乎正在混合三种完全不同的正交事物:

  • 静态与动态类型
  • 清单与隐式类型
  • 命名与匿名类型

这三个方面是完全独立的,彼此没有任何关系。

静态与动态类型是指何时进行类型检查:动态类型发生在运行时,静态类型发生在运行时之前

清单与隐式类型是指类型是否在源代码中显示:清单类型意味着程序员必须将类型写入源代码,隐式类型意味着类型系统自己计算出来。

命名与匿名类型是指类型是否具有名称。

C# 4.0 中的dynamic关键字意味着这个变量、参数、方法、字段、属性……无论是动态类型的,即它的类型将在运行时检查。所有不是动态类型的都是静态类型的。类型是静态的还是动态的不仅决定了何时进行类型检查,而且在 C# 4.0 中,它还决定了何时进行方法分派。在 C# 中,方法分派是在运行时之前完成的,基于静态类型(当然,运行时子类型多态性除外),而在 C# 4.0 中的动态类型对象上,方法分派是在运行时完成的,基于运行时类型。

C# 3.0 中的var关键字意味着这个局部变量将被隐式类型化,也就是说,不是程序员显式地写下类型,而是类型系统会自己计算出来。这与动态类型无关,至少在 C# 3.0 中是这样。变量将是强静态类型的,就像您自己写下类型一样。这只是为了方便:例如,当类型系统可以清楚地确定是 a时,为什么还要写下所有类型名称两次,所以改为写. 请注意,这没有任何动态或匿名的。该类型是静态的,并且有一个名称:HashMap<int, string> foo = new HashMap<int, string>();fooHashMap<int, string>var foo = new HashMap<int, string();HashMap<int, string>. 当然,在 C# 4.0 中,如果类型系统发现赋值的右侧是动态的,那么左侧变量的类型将是动态的。

C# 3.0 中的匿名类型意味着该类型没有名称。好吧,实际上,真正的匿名类型需要对通用类型系统进行向后不兼容的更改,所以在幕后实际发生的是编译器将为该类型生成一个非常长、非常随机、唯一且非法的名称,并放入该名称出现在匿名类型的任何位置。但是从程序员的角度来看,类型没有名字。为什么这很有用?嗯,有时你有中间结果,你只需要短暂的,然后再扔掉。给这些瞬态类型起一个自己的名字会将它们提升到一个他们根本不应该得到的重要程度。但同样,这没有什么动态的。

那么,如果类型没有名字,程序员怎么去引用呢?好吧,她不能!至少不是直接的。程序员可以做的是描述类型:它有两个属性,一个叫做 type 的“name”,string另一个叫做 type 的“id” int。那是我想要的类型,但我不在乎它叫什么。

这是碎片开始融合的地方。在 C# 中,您必须通过显式写下类型名称来声明局部变量的类型。但是,你怎么能写下一个没有名字的类型的名字呢?这就是var进来的地方:因为从 C# 3.0 开始,这实际上不再正确:您不再需要写下名称,您也可以告诉编译器来弄清楚。因此,虽然我在上面第一段中写的内容是正确的,隐式类型和匿名类型与其他类型没有任何关系,但如果没有隐式类型,匿名类型也将毫无用处。

但是请注意,相反的情况并非如此:隐式类型在没有匿名类型的情况下非常有用。var foo = HashMap<int, string>非常有意义,并且看不到匿名类型。

于 2008-12-24T20:14:43.657 回答
22

匿名类型是为您创建的真实的、编译器生成的类型。这样做的好处是编译器可以稍后将这种类型重新用于需要它的其他操作,因为它是一个 POCO。

我对动态类型的理解是它们是后期绑定的,这意味着 CLR(或 DLR)将在执行时评估对象,然后使用鸭子类型来允许或禁止成员访问该对象。

所以我想区别在于匿名类型是编译器可以看到但你只能使用的真正的 POCO,而动态类型是后期绑定的动态对象。

于 2008-12-24T14:34:24.187 回答
20

dynamic类型本质上是,但将在运行时通过 DLR 或其他提供程序(例如反射)object解析所有方法/属性/运算符等调用。

这使它很像带有 VB 的 VB Option Strict Off,并且使它非常适合调用 COM 或 DLR 类型。

动态编译时没有类型检查;显然,匿名类型是正确的静态类型、类型检查的野兽(你可以在反射器中看到它们,尽管它们并不漂亮)。

此外,匿名类型可以由编译器专门处理;dynamic需要广泛的运行时支持 - 所以匿名类型是 C# 的一项功能,但dynamic主要由 .NET 4.0 实现(有一些 C# 4.0 支持)。

于 2008-12-24T14:34:53.860 回答
7

有 3 次,有 3 位演员——每次一个。

  • 设计时 - 程序员
  • 编译时 - c# 编译器
  • 运行时 - .net 运行时

匿名类型由编译器声明和命名。此声明基于程序员的规范(他如何使用类型)。由于这些类型是在程序员离开进程之后命名的,因此它们对程序员来说似乎是无名的,因此是“匿名的”。

  • 程序员说:有些类型有名称和地址
  • 编译器说:有一个名为 xyz 的类型,具有 Name 和 Address 属性和字段,都是字符串。
  • 运行时说:我无法区分 xyz 和程序员制作的任何类型之间的任何区别。

c# 中的动态类型允许您调用在编译时可能存在或不存在的方法。这对于调用未编译的 python 或 javascript 很有用。

  • 程序员说:把这个汽车实例当作一个动态类型。现在,嘎嘎。
  • 编译器说:动态类型是吗?一定没问题。我不会抱怨,因为我无法检查。
  • 运行时尝试使 car 实例 quack。
于 2008-12-24T18:26:19.570 回答
3

没有什么比一个小代码更能解决问题的了:

// anonymous types
var anonType = new {Id = "123123123", Name = "Goku", Age = 30, DateAdded = new DateTime()};
// notice we have a strongly typed anonymous class we can access the properties with
Console.WriteLine($"Anonymous Type: {anonType.Id} {anonType.Name} {anonType.Age} {anonType.DateAdded}");
// compile time error
//anonType = 100;

// dynamic types
dynamic dynType = 100.01m;
Console.WriteLine($"Dynamic type: {dynType}");
// it's ok to change the type however you want
dynType = new List<DateTime>();
Console.WriteLine($"Dynamic type: {dynType}");

// mix dynamic and anonymous
dynamic dynamicAnonymousType = new {Id = 8000, FirstName = "Goku", Gender = "male", IsSuperSaiyan = true};
// Wasn't sure this would work but it does! However, you lose intellisense on the FirstName so you have to type it manually.
Console.WriteLine($"FirstName: {dynamicAnonymousType.FirstName}");
dynamicAnonymousType = 100;
Console.WriteLine(dynamicAnonymousType);
// runtime error
Console.WriteLine($"Id: {dynamicAnonymousType.FirstName}");
于 2017-02-23T22:19:52.710 回答