深入理解C# 3.x的新特性(1): Anonymous Type
2011-03-26 11:57
453 查看
在C#2.0中,引入了一个新的Feature:AnonymousMethod,允许我们已Inline的方式来定义Delegate,为Developer在Coding的时候带来了很大的便利。在C#3.0中,我们又有了另一个相似的Feature:AnonymousType。AnonymousType允许我们已Inline的方式的创建一个基于未知类型、具有所需数据结构的对象。
在传统的编程模式中,对象依赖于一个既定的Type,我们只能在Type的基础上创建相应的Instance。比如如果我们需要创建一个EmployeeInstance,前提是我们已经有了一个相应的EmplyeeType的定义。比如:
有了这样一个EmployeeType,我们才可以创建相应的EmployeeInstance。
注:在上面的Code中,实际上使用到了另外两个C#3.0的newfeature:Implicitlytypedlocalvariable&ObjectInitializer.
这样基于一个预先定义的Type的对象创建方式的一个最大的限制就是:对于我们需要创建的每一个对象,我们必先定于该对象对应的Type。AnonymousType有效地解决了这个问题。我认为AnonymousType主要是基于下面的目的而设计:
一个Type是对一个现实中实体的State(Data)和Behavior(Method)的抽象。对于一些仅仅只包含State(Data)的Type(这样对象通常作为DataPackage在Application各个Layer之间、以及一个分布式环境中各个Application之间进行数据的传递),我们关心的仅仅是这个由这些数据成员组成结构:Type由哪些数据成员构成,它们的名称是什么,具有怎样的数据类型。换句话说,这样的Data-basedType定义了一个DataStructure,相应地,我们可以说一个固定的DataStructure对应着一个特定的Type。而C#3.0的AnonymousType就提供了这样的实现:
Compiler通过我们在SourceCode定义的数据成员的具体结构为我们创建相应的Type。
比如我们现在需要一个在上面定义的Employee对象,实际上我们不是需要的一个TypeName叫做Employee的对象,而是需要一个具有如下特征的对象:该对象具有两个数据成员:ID&Name,他们的数据类型分别为GUID和string。在SourceCode中,我们通过以下各结构指定这种特征:
varv=new{ID=Guid.NewGuid(),Name="ZhangSan"};
我们仔细分析上面这段代码,实际上它包含两部分的信息的:
为CompilerType的创建定义一个数据结构。{}中的内容指明了:包含两个数据成员,第一个是名称为ID,第二个为Name(成员的顺序也是一个决定因素,也就是说{Name="ZhangSan",ID=Guid.NewGuid()}和{ID=Guid.NewGuid(),Name="ZhangSan"}对于AnonymousType将是不同的。我不太清楚这样的设计到底处于一个什么样的目的);和Implicitlytypedlocalvariable一样,成员的类型由指定的数据或者表达式计算结果的数据类型决定。
为在运行时对象的创建提供数据,就像Constructor的参数一样。
通过编译,Compiler将会创建一个名为<>f__AnonymousType0<<>j__AnonymousTypeTypeParameter1,<>j__AnonymousTypeTypeParameter2>的Class。该Class的结构如下:
<>j__AnonymousTypeTypeParameter1和<>j__AnonymousTypeTypeParameter2这两个GenericType代表我在{}中制定ID和Name的类型。通过这个结构,我们发现其定义和一般的GenericType并没有什么区别。
为了进一步了解生成什么样的AnonymousType,我们使用ILDASM在IL级别看看生成的AnonymousType的大体结构:
为了做一个对比,下面是我们最开始定义的NamedEmployeeType在ILDASM中的结构:
如果想更清楚了解AnonymousType的本质,建议读者亲自使用ILDASM看看的每个成员具体的IL。
在这个Sample中,我定义了两个Project:
ConsoleApplication:Artech.NewFeatureInCSharp.ConsoleApp
ClassLibray:Artech.NewFeatureInCSharp.Library
Artech.NewFeatureInCSharp.Library中定一个EmployeeType:
和一个Static的UtilityClass:
在Utility中定义了两个GetEmployee方法,分别返回以AnonymousType形式和NamedType形式的Employee对象。
代码不复杂,我在这里简单介绍一下整体的结构。这个结构分两部分,第一部分是基于AnonymousType的,另一部分是基于NamedEmployeeType的。在第一部分中,我首先创建了3个AnonymousType的Instance:v1、v2和v3(v3是通过调用定义在Artech.NewFeatureInCSharp.Library中的Utility获得,其余两个则直接通过Inline的方式创建),第二部分也具有相同的代码结构。
然后实现他们对应的Type的Fullname.
最后调用object.ReferenceEquals对这3个Type进行比较。
大家先想想到底运行后将会出现什么样的结果,看看你的想法和真实的结果是否一致:
对于第二部分基于NamedType的输出,结果很明显,没有什么好说的。我们重点来看基于AnonymousType的输出结果:
我们通过Inline的方式创建了v1和v2,通过调用定义在另一个Assembly中定义的Utilityclass创建了v3。虽然我们创建对象的方式不同,但是这3个Instance的结构完全相同,我们可以想象他们对应的Type应该相似。但是,他们到底是不是就是同一个Type呢?通过输出的Type的FullName:<>f__AnonymousType0`2[System.Guid,System.String]来看,他们“貌似”同一个Type。但是Fullname相同并不意味着他们就是同一个Type。确定两个Type的同一性的方法就是确定他们具有相同的Reference。于是我们使用了object.ReferenceEquals方法。两个调用的结果完全不同:v1和v2对应的Type是一样的,而v1和v3则不是同一个。关于Type在ManagedHeap的体现,请参阅我的文章:《[原创]Whatis"Type"inmanagedheap?》。
我们来讨论问什么会出现上面的运行结果。原因很简单:Compiler在生成AnonymousType的时候,并不是为每个形如这样{M1=?,M2=?,…}的结构生成一个不同的Type,它只会为不同的参数列表的结构:参数的名称,参数的数据类型,参数的相互顺序定义不同的Type。而具有相同的参数列表的{M1=?,M2=?,…}会共享同一个Type。但是这种机制仅限于在同一个Assembly中。也就是在一个Assembly创建的AnonymousType仅仅限于在本Assembly中使用,不能被另一个Assembly共享。所以我们通过Inline的方式创建了v1和v2是同一个Type的两个Instance,而我们通过跨Assembly创建的v3却属于不同的Type,尽管他们的Type定义可能完全一样。
作者:Artech
出处:http://artech.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
转自:/article/1308595.html
[b]一、AnonymousTypeOverview
[/b]在传统的编程模式中,对象依赖于一个既定的Type,我们只能在Type的基础上创建相应的Instance。比如如果我们需要创建一个EmployeeInstance,前提是我们已经有了一个相应的EmplyeeType的定义。比如:
publicclassEmployee
{
privateGuid_id;
privatestring_name;
publicGuidID
{
get{return_id;}
set{_id=value;}
}
publicstringName
{
get{return_name;}
set{_name=value;}
}
}
有了这样一个EmployeeType,我们才可以创建相应的EmployeeInstance。
1:varv=newEmployee{ID=Guid.NewGuid(),Name="ZhangSan"};
注:在上面的Code中,实际上使用到了另外两个C#3.0的newfeature:Implicitlytypedlocalvariable&ObjectInitializer.
这样基于一个预先定义的Type的对象创建方式的一个最大的限制就是:对于我们需要创建的每一个对象,我们必先定于该对象对应的Type。AnonymousType有效地解决了这个问题。我认为AnonymousType主要是基于下面的目的而设计:
一个Type是对一个现实中实体的State(Data)和Behavior(Method)的抽象。对于一些仅仅只包含State(Data)的Type(这样对象通常作为DataPackage在Application各个Layer之间、以及一个分布式环境中各个Application之间进行数据的传递),我们关心的仅仅是这个由这些数据成员组成结构:Type由哪些数据成员构成,它们的名称是什么,具有怎样的数据类型。换句话说,这样的Data-basedType定义了一个DataStructure,相应地,我们可以说一个固定的DataStructure对应着一个特定的Type。而C#3.0的AnonymousType就提供了这样的实现:
Compiler通过我们在SourceCode定义的数据成员的具体结构为我们创建相应的Type。
比如我们现在需要一个在上面定义的Employee对象,实际上我们不是需要的一个TypeName叫做Employee的对象,而是需要一个具有如下特征的对象:该对象具有两个数据成员:ID&Name,他们的数据类型分别为GUID和string。在SourceCode中,我们通过以下各结构指定这种特征:
varv=new{ID=Guid.NewGuid(),Name="ZhangSan"};
我们仔细分析上面这段代码,实际上它包含两部分的信息的:
为CompilerType的创建定义一个数据结构。{}中的内容指明了:包含两个数据成员,第一个是名称为ID,第二个为Name(成员的顺序也是一个决定因素,也就是说{Name="ZhangSan",ID=Guid.NewGuid()}和{ID=Guid.NewGuid(),Name="ZhangSan"}对于AnonymousType将是不同的。我不太清楚这样的设计到底处于一个什么样的目的);和Implicitlytypedlocalvariable一样,成员的类型由指定的数据或者表达式计算结果的数据类型决定。
为在运行时对象的创建提供数据,就像Constructor的参数一样。
二、CLR眼中的AnonymousType
我们说AnonymousType仅仅是C#3.0的新的特性,而没有说AnonymousType是.NETFramework3.5的新特性。这是因为AnonymousType仅仅是.NETProgrammingLanguage和相应的Compiler的新引入的特征。而对于.NETFramework3.5来说,它看不到这和原来有什么不同,换句话说,对于AnonymousType和一般的NamedType,对于CLR来说他们之间没有什么本质的区别。对于下面这样的一段简单的代码:1:varv=new{ID=Guid.NewGuid(),Name="ZhangSan"};
通过编译,Compiler将会创建一个名为<>f__AnonymousType0<<>j__AnonymousTypeTypeParameter1,<>j__AnonymousTypeTypeParameter2>的Class。该Class的结构如下:
1:publicsealedclass<>f__AnonymousType0<<>j__AnonymousTypeTypeParameter1,<>j__AnonymousTypeTypeParameter2>
2:{
3:public<>j__AnonymousTypeTypeParameter1ID{get;set;}
4:publicj__AnonymousTypeTypeParameter2Name{get;set;}
5:privatej__AnonymousTypeTypeParameter1<>i__AnonymousTypeField3;
6:privatej__AnonymousTypeTypeParameter2<>i__AnonymousTypeField4;
7:}
<>j__AnonymousTypeTypeParameter1和<>j__AnonymousTypeTypeParameter2这两个GenericType代表我在{}中制定ID和Name的类型。通过这个结构,我们发现其定义和一般的GenericType并没有什么区别。
为了进一步了解生成什么样的AnonymousType,我们使用ILDASM在IL级别看看生成的AnonymousType的大体结构:
为了做一个对比,下面是我们最开始定义的NamedEmployeeType在ILDASM中的结构:
如果想更清楚了解AnonymousType的本质,建议读者亲自使用ILDASM看看的每个成员具体的IL。
三、AnonymousTypeisBoundtoAssembly
在上面一个部分中我们说了对于CLR来说,AnonymousType和一般的NamedType并没有本质的区别。但是话不能太绝对,他们之间还是有一点小小的差异。到底是什么样差异,我在这里先卖一个关子。在具体介绍这个差异的时候,我们先来看看一个Sample:在这个Sample中,我定义了两个Project:
ConsoleApplication:Artech.NewFeatureInCSharp.ConsoleApp
ClassLibray:Artech.NewFeatureInCSharp.Library
Artech.NewFeatureInCSharp.Library中定一个EmployeeType:
1:publicclassEmployee
2:{
3:privateGuid_id;
4:privatestring_name;
5:
6:publicGuidID
7:{
8:get{return_id;}
9:set{_id=value;}
10:}
11:
12:publicstringName
13:{
14:get{return_name;}
15:set{_name=value;}
16:}
17:}
和一个Static的UtilityClass:
1:publicstaticclassUtility
2:{
3:publicstaticobjectAnonymous_GetEmployee(Guidid,stringname)
4:{
5:returnnew{ID=id,Name=name};
6:}
7:
8:publicstaticEmployeeGetEmployee(Guidid,stringname)
9:{
10:returnnewEmployee{ID=id,Name=name};
11:}
12:}
在Utility中定义了两个GetEmployee方法,分别返回以AnonymousType形式和NamedType形式的Employee对象。
1:classProgram
2:{
3:staticvoidMain(string[]args)
4:{
5:varv1=new{ID=Guid.NewGuid(),Name="ZhangSan"};
6:varv2=new{ID=Guid.NewGuid(),Name="LiSi"};
7:varv3=Utility.Anonymous_GetEmployee(Guid.NewGuid(),"WangWu");
8:Console.WriteLine("varv1=new{ID=Guid.NewGuid(),Name=/"ZhangSan/"};");
9:Console.WriteLine("varv2=new{ID=Guid.NewGuid(),Name=/"LiSi/"};");
10:Console.WriteLine("varv3=Utility.Anonymous_GetEmployee(Guid.NewGuid(),/"WangWu/");");
11:
12:Console.WriteLine("/nv1.GetType()={0}",v1.GetType());
13:Console.WriteLine("v2.GetType()={0}",v2.GetType());
14:Console.WriteLine("v3.GetType()={0}",v3.GetType());
15:
16:Console.WriteLine("/nobject.ReferenceEquals(v1.GetType(),v2.GetType())={0}",object.ReferenceEquals(v1.GetType(),v2.GetType()));
17:Console.WriteLine("object.ReferenceEquals(v1.GetType(),v3.GetType())={0}",object.ReferenceEquals(v1.GetType(),v3.GetType()));
18:
19:Console.WriteLine("/n/n");
20:
21:varv4=newEmployee{ID=Guid.NewGuid(),Name="ZhangSan"};
22:varv5=newEmployee{ID=Guid.NewGuid(),Name="LiSi"};
23:varv6=Utility.GetEmployee(Guid.NewGuid(),"WangWu");
24:Console.WriteLine("varv4=newEmployee{ID=Guid.NewGuid(),Name=/"ZhangSan/"};");
25:Console.WriteLine("varv5=newEmployee{ID=Guid.NewGuid(),Name=/"LiSi/"};");
26:Console.WriteLine("varv6=Utility.GetEmployee(Guid.NewGuid(),/"WangWu/");");
27:
28:Console.WriteLine("/nv4.GetType()={0}",v4.GetType());
29:Console.WriteLine("v5.GetType()={0}",v5.GetType());
30:Console.WriteLine("v6.GetType()={0}",v6.GetType());
31:
32:Console.WriteLine("/nobject.ReferenceEquals(v4.GetType(),v5.GetType())={0}",object.ReferenceEquals(v4.GetType(),v5.GetType()));
33:Console.WriteLine("object.ReferenceEquals(v4.GetType(),v6.GetType())={0}",object.ReferenceEquals(v4.GetType(),v6.GetType()));
34:}
35:}
代码不复杂,我在这里简单介绍一下整体的结构。这个结构分两部分,第一部分是基于AnonymousType的,另一部分是基于NamedEmployeeType的。在第一部分中,我首先创建了3个AnonymousType的Instance:v1、v2和v3(v3是通过调用定义在Artech.NewFeatureInCSharp.Library中的Utility获得,其余两个则直接通过Inline的方式创建),第二部分也具有相同的代码结构。
1:varv1=new{ID=Guid.NewGuid(),Name="ZhangSan"};
2:varv2=new{ID=Guid.NewGuid(),Name="LiSi"};
3:varv3=Utility.Anonymous_GetEmployee(Guid.NewGuid(),"WangWu");
然后实现他们对应的Type的Fullname.
1:Console.WriteLine("/nv1.GetType()={0}",v1.GetType());
2:Console.WriteLine("v2.GetType()={0}",v2.GetType());
3:Console.WriteLine("v3.GetType()={0}",v3.GetType());
最后调用object.ReferenceEquals对这3个Type进行比较。
1:Console.WriteLine("/nobject.ReferenceEquals(v1.GetType(),v2.GetType())={0}",object.ReferenceEquals(v1.GetType(),v2.GetType()));
2:Console.WriteLine("object.ReferenceEquals(v1.GetType(),v3.GetType())={0}",object.ReferenceEquals(v1.GetType(),v3.GetType()));
大家先想想到底运行后将会出现什么样的结果,看看你的想法和真实的结果是否一致:
对于第二部分基于NamedType的输出,结果很明显,没有什么好说的。我们重点来看基于AnonymousType的输出结果:
我们通过Inline的方式创建了v1和v2,通过调用定义在另一个Assembly中定义的Utilityclass创建了v3。虽然我们创建对象的方式不同,但是这3个Instance的结构完全相同,我们可以想象他们对应的Type应该相似。但是,他们到底是不是就是同一个Type呢?通过输出的Type的FullName:<>f__AnonymousType0`2[System.Guid,System.String]来看,他们“貌似”同一个Type。但是Fullname相同并不意味着他们就是同一个Type。确定两个Type的同一性的方法就是确定他们具有相同的Reference。于是我们使用了object.ReferenceEquals方法。两个调用的结果完全不同:v1和v2对应的Type是一样的,而v1和v3则不是同一个。关于Type在ManagedHeap的体现,请参阅我的文章:《
我们来讨论问什么会出现上面的运行结果。原因很简单:Compiler在生成AnonymousType的时候,并不是为每个形如这样{M1=?,M2=?,…}的结构生成一个不同的Type,它只会为不同的参数列表的结构:参数的名称,参数的数据类型,参数的相互顺序定义不同的Type。而具有相同的参数列表的{M1=?,M2=?,…}会共享同一个Type。但是这种机制仅限于在同一个Assembly中。也就是在一个Assembly创建的AnonymousType仅仅限于在本Assembly中使用,不能被另一个Assembly共享。所以我们通过Inline的方式创建了v1和v2是同一个Type的两个Instance,而我们通过跨Assembly创建的v3却属于不同的Type,尽管他们的Type定义可能完全一样。
作者:
出处:
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
转自:
相关文章推荐
- [原创]深入理解C# 3.x的新特性(3):从Delegate、Anonymous Method到Lambda Expression
- [原创]深入理解C# 3.x的新特性(3):从Delegate、Anonymous Method到Lambda Expression
- 深入理解C# 3.x的新特性系列总结
- 深入理解C# 3.x的新特性(1): Anonymous Type
- 【转】《深入理解C# 3.x的新特性》博文系列汇总
- [原创]深入理解C# 3.x的新特性(2):Extension Method - Part II
- 深入理解C# 3.x的新特性(2):Extension Method[下篇]
- 深入理解C# 3.x的新特性
- [原创-总结]深入理解C# 3.x的新特性系列总结
- [原创]深入理解C# 3.x的新特性(2):Extension Method - Part I
- 深入理解C# 3.x的新特性(2):Extension Method[上篇]
- 《深入理解C# 3.x的新特性》博文系列汇总
- 深入理解C#3.x的新特性(4):Automatically Implemented Property
- 深入理解C# 3.x的新特性(5):Object Initializer 和 Collection Initializer
- [原创]深入理解C# 3.x的新特性(1): Anonymous Type
- [原创]深入理解C#3.x的新特性(4):Automatically Implemented Property
- [原创]深入理解C# 3.x的新特性(5):Object Initializer 和 Collection Initializer
- C# 语言特性系列(3) 深入理解继承
- C# 语言特性系列(10) 深入理解默认值 Default Values
- C# 语言特性系列(4) 深入理解虚方法