Artech

Develop every application as an art using the most suitable technologies!

常用链接

统计

积分与排名

CnBlogs

专家的Blog|主页

最新评论

[原创]深入理解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的定义。比如:

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 = Guid.NewGuid(), 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 = Guid.NewGuid(), Name= "Zhang San" };

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

  • 为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来说他们之间没有什么本质的区别

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

var v = new{ID = Guid.NewGuid(), Name= "Zhang San" };

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

public sealed class <>f__AnonymousType0<<>j__AnonymousTypeTypeParameter1, <>j__AnonymousTypeTypeParameter2>

{

// Properties

public <>j__AnonymousTypeTypeParameter1IDgetset; }

public j__AnonymousTypeTypeParameter2 Namegetset; }

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



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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Artech.NewFeatureInCSharp.Library
{
    
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 System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Artech.NewFeatureInCSharp.Library
{
    
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 System.Collections.Generic;
using System.Linq;
using System.Text;
using Artech.NewFeatureInCSharp.Library;

namespace Artech.NewFeatureInCSharp.ConsoleApp
{
    
class Program
    
{
        
static void Main(string[] args)
        
{
            var v1 
= new{ID