深入理解C30的新特性
在C#2.0中,引入了一个新的Feature:Anonymous Method,允许我们已Inline的方式来定义Delegate,为Developer在Coding的时候带来了很大的便利。在C#3.0中,我们又有了另一个相似的Feature:Anonymous Type。Anonymous Type允许我们已Inline的方式的创建一个基于未知类型、具有所需数据结构的对象。
一、Anonymous Type Overview
在传统的编程模式中,对象依赖于一个既定的Type,我们只能在Type的基础上创建相应的Instance。比如如果我们需要创建一个Employee Instance,前提是我们已经有了一个相应的Emplyee Type的定义。比如:
public class Employee
{
private Guid _id;
private string _name;
public Guid ID
{
get { return _id; }
set { _id = value; }
}
public string Name
{
get { return _name; }
set { _name = value; }
}
}
有了这样一个Employee Type,我们才可以创建相应的Employee Instance。
var v = new Employee{ ID = wGuid(), Name = "Zhang San" };
注:在上面的Code中,实际上使用到了另外两个C# 3.0的new feature: Implicitly typed local variable Object Initializer.
这样基于一个预先定义的Type的对象创建方式的一个最大的限制就是:对于我们需要创建的每一个对象,我们必先定于该对象对应的Type。Anonymous Type有效地解决了这个问题。我认为Anonymous Type主要是基于下面的目的而设计:
一个Type是对一个现实中实体的State(Data)和Behavior(Method)的抽象。对于一些仅仅只包含State(Data)的Type(这样对象通常作为Data Package在Application各个Layer之间、以及一个分布式环境中各个Application之间进行数据的传递),我们关心的仅仅是这个由这些数据成员组成结构:Type由哪些数据成员构成,它们的名称是什么,具有怎样的数据类型。换句话说,这样的Data-based Type定义了一个Data Structure,相应地,我们可以说一个固定的Data Structure对应着一个特定的Type。而C# 3.0 的Anonymous Type就提供了这样的实现:Compiler通过我们在Source Code定义的数据成员的具体结构为我们创建相应的Type。
比如我们现在需要一个在上面定义的Employee对象,实际上我们不是需要的一个Type Name叫做Employee的对象,而是需要一个具有如下特征的对象:该对象具有两个数据成员: ID Name,他们的数据类型分别为GUID和string。在Source Code中,我们通过以下各结构指定这种特征:
var v = new{ID = wGuid(), Name= "Zhang San" };
我们仔细分析上面这段代码,实际上它包含两部分的信息的:
为Compiler Type的创建定义一个数据结构。{}中的内容指明了:包含两个数据成员,第一个是名称为ID,第二个为Name(成员的顺序也是一个决定因素,也就是说{ID = Name= "Zhang San" ,wGuid()}和{ID = wGuid(), Name= "Zhang San" }对于的Anonymous Type将是不同的。我不太清楚这样的设计到底处于一个什么样的目的);和Implicitly typed local variable一样,成员的类型由指定的数据或者表达式计算结果的数据类型决定。
为在运行时对象的创建提供数据,就像Constructor的参数一样。
二、CLR 眼中的Anonymous Type
我们说Anonymous Type仅仅是C# 3.0的新的特性,而没有说Anonymous Type是.NET Framework 3.5的新特性。这是因为Anonymous Type仅仅是.NET Programming Language和相应的Compiler的新引入的特征。而对于.NET Framework 3.5来说,它看不到这和原来有什么不同,换句话说,对于Anonymous Type和一般的Named Type,对于CLR来说他们之间没有什么本质的区别。
对于下面这样的一段简单的代码:
var v = new{ID = wGuid(), Name= "Zhang San" };
通过编译节目爱奇艺播放指数从83,Compiler将会创建一个名为f__AnonymousType0j__AnonymousTypeTypeParameter1, j__AnonymousTypeTypeParameter2的Class。该Class的结构如下:
public sealed class f__AnonymousType0j__AnonymousTypeTypeParameter1, j__AnonymousTypeTypeParameter2
{
// Properties
public j__AnonymousTypeTypeParameter1ID{ get; set; }
public j__AnonymousTypeTypeParameter2 Name{ get; set; }
// Fields
private j__AnonymousTypeTypeParameter1 i__AnonymousTypeField3;
private j__AnonymousTypeTypeParameter2 i__AnonymousTypeField4;
}
j__AnonymousTypeTypeParameter1和j__AnonymousTypeTypeParameter2这两个Generic Type代表我在 {} 中制定ID和Name的类型。通过这个结构,我们发现其定义和一般的Generic Type并没有什么区别。
为了进一步了解生成什么样的Anonymous Type,我们使用IL DASM在IL级别看看生成的Anonymous Type的大体结构:
为了做一个对比,下面是我们最开始定义的Named Employee Type在IL DASM中的结构:
如果想更清楚了解Anonymous Type的本质,建议读者亲自使用IL DASM看看的每个成员具体的IL。
三、Anonymous Type is Bound to Assembly
在上面一个部分中我们说了对于CLR来说,Anonymous Type和一般的Named Type并没有本质的区别。但是话不能太绝对,他们之间还是有一点小小的差异。到底是什么样差异,我在这里先卖一个关子。在具体介绍这个差异的时候,我们先来看看一个Sample:
在这个Sample中,我定义了两个Project:
Console Application:nsoleApp
Class Libray:brary
brary中定一个Employee Type:
using System;
using neric;
using nq;
using xt;
namespace brary
{
public class Employee
{
private Guid _id;
private string _name;
public Guid ID
{
get { return _id; }
set { _id = value; }
}
public string Name
{
get { return _name; }
set { _name = value; }
}
}
}
和一个Static的Utility Class:
using System;
using neric;
using nq;
using xt;
namespace brary
{
public static class Utility
{
public static object Anonymous_GetEmployee(Guid id, string name)
{
return new { ID = id, Name = name };
}
public static Employee GetEmployee(Guid id, string name)
{
return new Employee { ID = id, Name = name };
}
}
}
在Utility中定义了两个GetEmployee方法,分别返回以Anonymous Type形式和Named Type形式的Employee对象。
using System;
using neric;
using nq;
using xt;
using brary;
namespace nsoleApp
{
class Program
{
static void Main(string[] args)
{
var v1 = new{ID = wGuid(), Name= "Zhang San" };
var v2 = new { ID = wGuid(), Name = "Li Si"};
var v3 = onymous_GetEmployee(wGuid(), "Wang Wu");
iteLine("var v1 = new{ID = wGuid(), Name= \"Zhang San\" };");
iteLine("var v2 = new{ID = wGuid(), Name= \"Li Si\" };");
iteLine("var v3 = onymous_GetEmployee(wGuid(), \"Wang Wu\");");
iteLine("\tType() = {0}", tType());
iteLine("tType() = {0}", tType());
iteLine("tType() = {0}", tType());
iteLine("\ferenceEquals(tType(),tType()) = {0}",ferenceEquals(tType(),tType()));
iteLine("ferenceEquals(tType(),tType()) = {0}", ferenceEquals(tType(), tType()));
iteLine("\n\n");
var v4 = new Employee { ID = wGuid(), Name = "Zhang San" };
var v5 = new Employee { ID = wGuid(), Name = "Li Si" };
var v6 = tEmployee(wGuid(), "Wang Wu");
iteLine("var v4 = new Employee{ID = wGuid(), Name= \"Zhang San\" };");
iteLine("var v5 = new Employee{ID = wGuid(), Name= \"Li Si\" };");
iteLine("var v6 = tEmployee(wGuid(), \"Wang Wu\");");
iteLine("\tType() = {0}", tType());
iteLine("tType() = {0}", tType());
iteLine("tType() = {0}"从经济增长的政策来看, tType());
iteLine("\ferenceEquals(tType(),tType()) = {0}", ferenceEquals(tType(), tType()));
iteLine("ferenceEquals(tType(),tType()) = {0}", ferenceEquals(tType(), tType()));
}
}
}
代码不复杂,我在这里简单介绍一下整体的结构。这个结构分两部分,第一部分是基于Anonymous Type的,另一部分是基于Named Employee Type的。在第一部分中,我首先创建了3个Anonymous Type的Instance:v1、v2和v3(v3是通过调用定义在brary中的Utility获得,其余两个则直接通过Inline的方式创建),第二部分也具有相同的代码结构。
var v1 = new{ID = wGuid(), Name= "Zhang San" };
var v2 = new { ID = wGuid(), Name = "Li Si"};
var v3 = onymous_GetEmployee(wGuid(), "Wang Wu");
然后现实他们对应的Type的Full name.
iteLine("\tType() = {0}", tType());
iteLine("tType() = {0}", tType());
iteLine("tType() = {0}", tType());
最后调用ferenceEquals对这3个Type进行比较。
iteLine("\ferenceEquals(tType(),tType()) = {0}",ferenceEquals(tType(),tType()));
iteLine("ferenceEquals(tType(),tType()) = {0}", ferenceEquals(tType(), tType()));
大家先想想到底运行后将会出现什么样的结果,看看你的想法和真实的结果是否一致:
对于第二部分基于Named Type的输出,结果很明显,没有什么好说的。我们重点来看基于Anonymous Type的输出结果:
我们通过Inline的方式创建了v1和v2,通过调用定义在另一个Assembly中定义的Utility class创建了v3。虽然我们创建对象的方式不同,但是这3个Instance的结构完全相同,我们可以想象他们对应的Type应该相似。但是,他们到底是不是就是同一个Type呢?通过输出的Type的Full Name:f__AnonymousType0`2[id,ring]来看,他们“貌似”同一个Type。但是Full name相同并不意味着他们就是同一个Type。确定两个Type的同一性的方法就是确定他们具有相同的Reference。于是我们使用了ferenceEquals方法。两个调用的结果完全不同:v1和v2对应的Type是一样的,而v1和v3则不是同一个。关于Type在Managed Heap的体现,请参阅我的文章: 《[原创]What is "Type" in managed heap?》。
我们来讨论问什么会出现上面的运行结果。原因很简单:Compiler在生成Anonymous Type的时候,并不是为每个形如这样{M1=?, M2 =? , …}的结构生成一个不同的Type,它只会为不同的参数列表的结构:参数的名称,参数的数据类型,参数的相互顺序定义不同的Type。而具有相同的参数列表的{M1=?, M2 =? , …}会共享同一个Type。但是这种机制仅限于在同一个Assembly中。也就是在一个Assembly创建的Anonymous Type仅仅限于在本Assembly中使用,不同被另一个Assembly共享。所以我们通过Inline的方式创建了v1和v2是同一个Type的两个Instance,而我们通过跨Assembly创建的v3却属于不同的Type,尽管他们的Type定义可能完全一样。
查看本文来源
松原白癜风医院他达拉非片能空腹吃吗焦作白癜风医院有哪些- 上一页:海岛奇兵大地图将改变 未来或将增加新水晶
- 下一页:装修专家问答实录
-
世界性唯一可供私藏!7600万年前戈尔冈龙骨架将拍卖
亚太地区唯一可供偷盗,7600万年前卡尼冈龙管状将出售。美联社另据,一具...
2025-04-09
-
新进展!国际研究团队发现"渐冻症"疗法新靶点
计算机科学在抑制剂研发中的极为重要日益重要的配角。近日,中的国学者策...
2025-02-04
-
糖尿病人吃二甲双胍,吃出一堆类药物,医生:吃药时要注意这4点
杨家韩近半年来胖了不少,取而代之肥胖症的双腿,也能勉强看出轮廓来了,...
2025-01-31
-
一场3000年的发展史趋势逆转
校对:方杰锋关于华北南部城镇人口问题,大家慢慢有了一个协商:最近2年...
2024-12-30
-
南柱赫被疑为网络霸凌女同学 曾参与12人群聊辱骂
南柱赫博客娱乐发信 日韩艺人南柱赫的一名女性高年级同学亦同在讨论区发...
2024-12-30
-
李健、李克勤志同道合,封闭式合作,李健风趣总结:顺应民意
《声生不息》毫无新意视为“声音合奏天花板”。李明和关淑怡结交,“双李...
2024-12-30