C# 3.0 新特性 学习(二):匿名类型、扩展方法
这两天看了一下msdnwebcast上的visual studio 2008的系列课程,记录下所学的知识,以便加深记忆
1.匿名类型
顾名思义 匿名类型就是没有名字的类型。在C#3.0中允许我们在程序中声明一个临时的类型来存储数据,例如:
2 {
3 static void Main(string[] args)
4 {
5 //声明一个匿名对象,拥有 Name和Age 属性
6 var obj = new { Name = "Joey", Age = 25 };//这里的new { Name = "Joey", Age = 25 } 就是一个匿名类型 ,obj则是这个类型的一个对象,称为匿名对象
7 Console.WriteLine("匿名对象obj : Name=" + obj.Name + " , Age=" + obj.Age);
8 }
9 }
上述代码中,我们声明了一匿名对象obj ,然后输出对象的属性值。如果在VS 你将鼠标移到 obj前面的var 上面,vs 会提示:obj 是一个匿名类型 ‘a 。这个‘a 是编译器自动作为标识的一个类型,匿名对象在编译时,编译器还是得给它一个类型。其实上面的匿名类型 new { Name = "Joey", Age = 25 } 是直接从Object继承过来的, 相当于
public class 'a{
public string Name{get;private set;}
public int Age{get;private set;}
}
这样的一个拥有只读属性的自定义类型。
在MSDN 中匿名类型的定义是这样的:
1.匿名类型提供了一种方便的方法,可用来将一组只读属性封装到单个对象中,而无需首先显式定义一个类型。
2.类型名由编译器生成,并且不能在源代码级使用。每个属性的类型由编译器推断。
3.可通过使用 new 运算符和对象初始值创建匿名类型。
上面三句话是所谓的”说到点子上了”.让人一看就明白匿名类型。但是在这里,我还得提一提,匿名类型和var 隐式类型化的声明关键字的关系;很多新手在看到var 声明时,就觉得这是一个匿名对象,匿名类型的对象是必须用var 来声明,但是用var 声明的对象不一定都是匿名对象,例如 var n=5; 你就不能说n 是一个匿名对象,n只是一个隐式类型化的局部变量;而 var s=new{ S1="abc",S2="def"}; s 则是一个 类型为匿名类型的对象。也就是说 匿名对象是 用var 声明的一个 在内存中临时的类型的对象。它的类型不能像隐式类型那样根据右边的实例画表达式来推断类型,它是一个实实在在的匿名类型,而var i=5; 这种隐式类型的声明,编译时,i其实还是 int32 类型,隐式类型只是一种语法糖。
那匿名类型一般都在什么情况下使用呢?
1.匿名类型通常用在查询表达式的 select 子句中,以便返回源序列中每个对象的属性子集(Linq 中使用的比较多)
匿名类型包含一个或多个公共只读属性。 包含其他种类的类成员(如方法或事件)为无效。 用来初始化属性的表达式不能为 null、匿名函数或指针类型。
最常见的方案是用其他类型的属性初始化匿名类型。在下面的示例中,假定名为 Product 的类存在。 类 Product 包括 Color 和 Price 属性,以及您不感兴趣的其他属性。 变量 products 是 Product 对象的集合。 匿名类型声明以 new 关键字开始。 声明初始化了一个只使用 Product 的两个属性的新类型。 这将导致在查询中返回较少数量的数据。
如果您没有在匿名类型中指定成员名称,编译器会为匿名类型成员指定与用于初始化这些成员的属性相同的名称。 必须为使用表达式初始化的属性提供名称,如下面的示例所示。 在下 面示例中,匿名类型的属性名称都为 Color 和 Price。
select new { prod.Color, prod.Price };
foreach (var v in productQuery)
{
Console.WriteLine("Color={0}, Price={1}", v.Color, v.Price);
}
可通过将隐式键入的本地变量与隐式键入的数组相结合创建匿名键入的元素的数组,如下面的示例所示。
var anonArray = new[] { new { name = "apple", diam = 4 }, new { name = "grape", diam = 1 }};
上面的代码是将两个匿名对象存入一个匿名的数组中。需要注意的是,在上面代码中,匿名数组存储的匿名对象的初始化器里的属性类型和属性名称和属性的数量必须一致。也就是说
new { name = "apple", diam = 4 }=new { name = "grape", diam = 1 } 成立的条件是: 属性名字和属性存储的值的类型以及属性个数必须全部相同,vs 编译器才会认为这两个匿名类型是同一个类型。这样的两个匿名类型才能够存入 anonArray 这样的一个匿名数组中。因为匿名数组也只能存储同一种类型的匿名对象。
2.扩展方法
2.Extension Method仅仅是看起来像是一个类型的方法,但其实质上不是,它更像是静态类型的静态方法,事实上,它确实拥有静态方法所具有的所有功能 。这点在下面实现扩展方法的时候就会明白,其实扩展方法就是一静态方法。
3.Extension Method的作用域是整个namespace可见的,并且可以通过using namespace来导入其它命名空间中的Extension Method
4.编译器生成的中间语言(IL) 会将代码转换为对静态方法的调用。因此,并未真正违反封装原则。
5.实际上,扩展方法无法访问它们所扩展的类型中的私有变量。
约定:
1.可以使用扩展方法来扩展类或接口,但不能重写扩展方法。
2.与接口或类方法具有相同名称和签名的扩展方法永远不会被调用。
3.编译时,扩展方法的优先级总是比类型本身中定义的实例方法低。
换句话说,如果某个类型具有一个名为Process(int i) 的方法,而您有一个具有相同签名的扩展方法,则编译器总是绑定该实例方法。
4.当编译器遇到方法调用时,它首先在该类型的实例方法中寻找匹配的方法。
如果未找到任何匹配方法,编译器将搜索为该类型定义的任何扩展方法,并且绑定到它找到的第一个扩展方法
MSDN中是这么定义的:
扩展方法使您能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。扩展方法是一种特殊的静态方法,但可以像扩展类型上的实例方法一样进行调用。
Smaple:
1 namespace Demo
3 class Program
4 {
5 static void Main(string[] args)
6 {
7 var stu = new Student() { Name = "joey", Age = 25 };
8 //调用实例方法
9 Console.WriteLine(stu.ToString());
10 //调用扩展方法 。调用的时候会vs 智能感知会在方法边上加个向下的箭头,表示这是一个扩展方法
11 Console.WriteLine(stu.Hello());
12 }
13 }
14 public class Student
15 {
16 public string Name { get; set; }
17 public int Age { get; set; }
18 //重写ToString方法
19 public new string ToString()
20 {
21 return "Name: " + this.Name + "\nAge: " + this.Age;
22 }
23 }
24
25 public static class ExtendMehods
26 {
27 //使用this 关键字扩展类的方法
28 public static string Hello(this Student stu)
29 {
30 return "嗨!大家好!我叫 " + stu.Name + ",我今年 " + stu.Age + " 岁";
31 }
32 }
33 }
posted on 2012-10-25 16:06 Joey_zy0210 阅读(11212) 评论(0) 编辑 收藏 举报