`
varsoft
  • 浏览: 2442675 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

[原创]深入理解C# 3.x的新特性(1): Anonymous Type

阅读更多
在C#3.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的定义。比如:

publicclassEmployee
{
privateGuid_id;
privatestring_name;

publicGuidID
{
get{return_id;}
set{_id=value;}
}


publicstringName
{
get{return_name;}
set{_name=value;}
}

}

有了这样一个Employee Type,我们才可以创建相应的Employee Instance。

varv=newEmployee{ID=Guid.NewGuid(),Name="ZhangSan"};

注:在上面的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中,我们通过以下各结构指定这种特征:

varv=new{ID=Guid.NewGuid(),Name="ZhangSan"};

我们仔细分析上面这段代码,实际上它包含两部分的信息的:

  • 为Compiler Type的创建定义一个数据结构。{}中的内容指明了:包含两个数据成员,第一个是名称为ID,第二个为Name(成员的顺序也是一个决定因素,也就是说{ID = Name= "Zhang San" ,Guid.NewGuid()}和{ID = Guid.NewGuid(), 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来说他们之间没有什么本质的区别

对于下面这样的一段简单的代码:

varv=new{ID=Guid.NewGuid(),Name="ZhangSan"};

通过编译,Compiler将会创建一个名为<>f__AnonymousType0<<>j__AnonymousTypeTypeParameter1, <>j__AnonymousTypeTypeParameter2>的Class。该Class的结构如下:

publicsealedclass<>f__AnonymousType0<<>j__AnonymousTypeTypeParameter1,<>j__AnonymousTypeTypeParameter2>

{

//Properties

public<>j__AnonymousTypeTypeParameter1ID{get;set;}

publicj__AnonymousTypeTypeParameter2Name{get;set;}

//Fields

privatej__AnonymousTypeTypeParameter1<>i__AnonymousTypeField3;

privatej__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:Artech.NewFeatureInCSharp.ConsoleApp
  • Class Libray:Artech.NewFeatureInCSharp.Library



Artech.NewFeatureInCSharp.Library中定一个Employee Type:

usingSystem;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Text;

namespaceArtech.NewFeatureInCSharp.Library
{
publicclassEmployee
{
privateGuid_id;
privatestring_name;

publicGuidID
{
get{return_id;}
set{_id=value;}
}


publicstringName
{
get{return_name;}
set{_name=value;}
}

}

}

和一个Static的Utility Class:

usingSystem;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Text;

namespaceArtech.NewFeatureInCSharp.Library
{
publicstaticclassUtility
{
publicstaticobjectAnonymous_GetEmployee(Guidid,stringname)
{
returnnew{ID=id,Name=name};
}


publicstaticEmployeeGetEmployee(Guidid,stringname)
{
returnnewEmployee{ID=id,Name=name};
}

}

}

在Utility中定义了两个GetEmployee方法,分别返回以Anonymous Type形式和Named Type形式的Employee对象。

usingSystem;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Text;
usingArtech.NewFeatureInCSharp.Library;

namespaceArtech.NewFeatureInCSharp.ConsoleApp
{
classProgram
{
staticvoidMain(string[]args)
{
varv1
=new{ID=Guid.NewGuid(),Name="ZhangSan"};
varv2
=new{ID=Guid.NewGuid(),Name="LiSi"};
varv3
=Utility.Anonymous_GetEmployee(Guid.NewGuid(),"WangWu");
Console.WriteLine(
"varv1=new{ID=Guid.NewGuid(),Name=\"ZhangSan\"};");
Console.WriteLine(
"varv2=new{ID=Guid.NewGuid(),Name=\"LiSi\"};");
Console.WriteLine(
"varv3=Utility.Anonymous_GetEmployee(Guid.NewGuid(),\"WangWu\");");

Console.WriteLine(
"\nv1.GetType()={0}",v1.GetType());
Console.WriteLine(
"v2.GetType()={0}",v2.GetType());
Console.WriteLine(
"v3.GetType()={0}",v3.GetType());

Console.WriteLine(
"\nobject.ReferenceEquals(v1.GetType(),v2.GetType())={0}",object.ReferenceEquals(v1.GetType(),v2.GetType()));
Console.WriteLine(
"object.ReferenceEquals(v1.GetType(),v3.GetType())={0}",object.ReferenceEquals(v1.GetType(),v3.GetType()));

Console.WriteLine(
"\n\n");

varv4
=newEmployee{ID=Guid.NewGuid(),Name="ZhangSan"};
varv5
=newEmployee{ID=Guid.NewGuid(),Name="LiSi"};
varv6
=Utility.GetEmployee(Guid.NewGuid(),"WangWu");
Console.WriteLine(
"varv4=newEmployee{ID=Guid.NewGuid(),Name=\"ZhangSan\"};");
Console.WriteLine(
"varv5=newEmployee{ID=Guid.NewGuid(),Name=\"LiSi\"};");
Console.WriteLine(
"varv6=Utility.GetEmployee(Guid.NewGuid(),\"WangWu\");");

Console.WriteLine(
"\nv4.GetType()={0}",v4.GetType());
Console.WriteLine(
"v5.GetType()={0}",v5.GetType());
Console.WriteLine(
"v6.GetType()={0}",v6.GetType());

Console.WriteLine(
"\nobject.ReferenceEquals(v4.GetType(),v5.GetType())={0}",object.ReferenceEquals(v4.GetType(),v5.GetType()));
Console.WriteLine(
"object.ReferenceEquals(v4.GetType(),v6.GetType())={0}",object.ReferenceEquals(v4.GetType(),v6.GetType()));
}

}

}

代码不复杂,我在这里简单介绍一下整体的结构。这个结构分两部分,第一部分是基于Anonymous Type的,另一部分是基于Named Employee Type的。在第一部分中,我首先创建了3个Anonymous Type的Instance:v1、v2和v3(v3是通过调用定义在Artech.NewFeatureInCSharp.Library中的Utility获得,其余两个则直接通过Inline的方式创建),第二部分也具有相同的代码结构。

varv1=new{ID=Guid.NewGuid(),Name="ZhangSan"};
varv2
=new{ID=Guid.NewGuid(),Name="LiSi"};
varv3
=Utility.Anonymous_GetEmployee(Guid.NewGuid(),"WangWu");

然后现实他们对应的Type的Full name.

Console.WriteLine("\nv1.GetType()={0}",v1.GetType());
Console.WriteLine(
"v2.GetType()={0}",v2.GetType());
Console.WriteLine(
"v3.GetType()={0}",v3.GetType());

最后调用object.ReferenceEquals对这3个Type进行比较。

Console.WriteLine("\nobject.ReferenceEquals(v1.GetType(),v2.GetType())={0}",object.ReferenceEquals(v1.GetType(),v2.GetType()));
Console.WriteLine(
"object.ReferenceEquals(v1.GetType(),v3.GetType())={0}",object.ReferenceEquals(v1.GetType(),v3.GetType()));

大家先想想到底运行后将会出现什么样的结果,看看你的想法和真实的结果是否一致:

对于第二部分基于Named Type的输出,结果很明显,没有什么好说的。我们重点来看基于Anonymous Type的输出结果:

我们通过Inline的方式创建了v1和v2,通过调用定义在另一个Assembly中定义的Utility class创建了v3。虽然我们创建对象的方式不同,但是这3个Instance的结构完全相同,我们可以想象他们对应的Type应该相似。但是,他们到底是不是就是同一个Type呢?通过输出的Type的Full Name:<>f__AnonymousType0`2[System.Guid,System.String]来看,他们“貌似”同一个Type。但是Full name相同并不意味着他们就是同一个Type。确定两个Type的同一性的方法就是确定他们具有相同的Reference。于是我们使用了object.ReferenceEquals方法。两个调用的结果完全不同: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定义可能完全一样。

C# 3.x相关内容:
[原创]深入理解C# 3.x的新特性(1):Anonymous Type
http://www.cnblogs.

评论

相关推荐

    [详细完整版]路由器用户名.doc

    路由器用户名 篇一:常见路由器默认登录用户名和密码 常见路由器默认登录用户名和密码(大全) 艾玛 701g :192.168.101.1 或 192.168.0.1 用户名:admin 密码:admin 或 用户名: SZIM 密码:SZIM 艾玛701H: 192....

    Apress.Accelerated.C#.2008.pdf

    CHAPTER 1 C# Preview ...............1 CHAPTER 2 C# and the CLR ..............9 CHAPTER 3 C# Syntax Overview............17 CHAPTER 4 Classes, Structs, and Objects ..........39 CHAPTER 5 Interfaces and ...

    EurekaLog_7.5.1.0_Enterprise

    EurekaLog 7.5 update 1 (7.5.1.0), 24-November-2016 1)....Important: EurekaLog 6 compatibility mode was completely removed 2)....Added: --el_debug_standalone option to have separate .log files for each...

    InsightSphinxExamples-5.0.0.zip

    - Anonymous online editing. - Editable online with CodeMirror. - See `Github Issues`_ for a complete list of actions to be performed, bugs, *etc.* Build dependencies ------------------ Required ^^^...

    [详细完整版]路由器的设置.txt

    路由器密码破解 上海贝尔:用户名:useradmin 密码:3cpuw 用户:051185015000 密码: 艾玛 701g192.168.101.1 192.168.0.1用户名:admin 密码:admin用户名:SZIM 密码:SZIM, 艾玛701H192.168.1.1 10.0.0.2用户名...

    路由器初始密码.doc

    "路由器初始密码 " 路由器初始密码 艾玛 701g 192.168.101.1 192.168.0.1 用户名:admin 密码:admin 用户名:SZIM 密码:SZIM 艾玛701H 192.168.1.1 10.0.0.2 用户名:admin 密码:epicrouter 实达2110EH ROUTER ...

    路由器默认帐号密码大全-路由器默认账号密码.docx

    路由器默认帐号密码大全 路由器默认账号密码 路由器默认帐号密码大全-路由器默认账号密码全文共3页,当前为第1页。005-01-31 17:26:16 艾玛 701g 192.168.101.1 192.168.0.1 用户名:admin 密码:admin 用户名:SZIM...

    Wrox.Professional.JavaScript.for.Web.Developers.2nd.Edition.Jan.2009.pdf

    .1 Chapter 2: JavaScript in HTML 13 Chapter 3: Language Basics .23 Chapter 4: Variables, Scope, and Memory 79 Chapter 5: Reference Types 97 Chapter 6: Object-Oriented Programming .151 Chapter 7: ...

    sourceinsight4087

    Fix: C++: Anonymous namespaces were not parsed correctly. Fix: C/C++: designated initializers were not being parsed. Fix: C++: Templated class was not parsed correctly for member functions. For ...

    emqx-ubuntu16.04-v3.2.6.zip

    MQTT EMQX ubuntu 16.04 下验证可以用版本 v3.2.6 使用方法 1. unzip and run $ unzip emqx-ubuntu14.04-v3.0.0.zip $ ./bin/emqx start emqx 3.1.0 is started successfully! $ ./bin/emqx_ctl status Node 'emq...

    cmd命令.docx

     69.type[type-name]:设置文件传输类型为type-name,缺省为ascii,如:type binary,设置二进制传输方式。  70.umask[newmask]:将远程服务器的缺省umask设置为newmask,如:umask 3  71.user user-name[password...

    jQuery常见问题及用法

    常用的jQuery的用法,及jQuery的使用误区。是快速熟悉jQuery的好的文档

    Bentley Microstation V8i (SELECTSeries 3) 08.11.09.578

    Bentley Microstation V8i (SELECTSeries 3) 08.11.09.578 Patch. Build date:04/03/2014 ...Cracker's names:Anonymous.ING/LAVteam Instructions: 1, Unrar and install the app. 2, Apply patch (run as admin).

    programing C# edition5

    1. C# 3.0 and .NET 3.5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 The Evolution of C# 3 The C# Language 4 The .NET Platform 6 2. Getting ...

    VisualAssistX.10.9.2068_含破解程序

    本人整理的最新版本VA及其破解。 使用方法: 1. 安装 VA_X_Setup2068.exe...ANONYMOUS. (255-user license) Support ends 2099.12.31 00001J-169QFR-ABFG9B-07WUFC-3PTNN3-YQECTW-6DY7DU-7WMV31-XK7978-AFHD17-68PH6B

    ScalaByExample.pdf

    1 Introduction 1 2 A First Example 3 3 Programming with Actors and Messages 7 4 Expressions and Simple Functions 11 4.1 Expressions And Simple Functions . . . . . . . . . . . . . . . . . . . . . . 11 ...

    Functional C#[January 2017].pdf

    Chapter 1, Tasting Functional Style in C#, introduces the functional programming approach by discussing its concepts and the comparison between functional and imperative programming. We also try to ...

    C#4.0本质论(第3版)

    Chapter 3: Operators and Control Flow Chapter 4: Methods and Parameters Chapter 5: Classes Chapter 6: Inheritance Chapter 7: Interfaces Chapter 8: Value Types Chapter 9: Well-Formed Types Chapter 10: ...

    Pro LINQ: Language Integrated Query in C# 2010

    * How to leverage all the new LINQ relevant C# 2008 language features including extension methods, lambda expressions, anonymous data types, and partial methods. * How to use LINQ to Objects to ...

Global site tag (gtag.js) - Google Analytics