代码改变世界

回顾 .NET 3.0/3.5特性(1)

2012-05-20 16:16  木木子  阅读(226)  评论(0编辑  收藏  举报

Object Initializers 对象初始化器

之前,经常这样初始化一个对象:

User user = new User();
user.Age = 23;
user.Name = "sanity";

有了3.0后,就这样初始化对象了:

User user = new User { Age = 23, Name = "Sanity" };

对象初始化器把代码变得简洁和优美了。

本质分析——对象初始化器,反编译结果:

User <>g__initLocal0 = new User {
    Age = 0x17,
    Name = "Sanity"
};
User user = <>g__initLocal0;
 

从反编译结果看出来,编译器对代码初始化器做了处理:声明一个User的临时变量 <>g__initLocal0,对这个临时变量赋值,然后将初始化好的临时变量赋给user变量。

 

Collection Initializers 集合初始化器

2.0中,我们初始化一个list,通过Add方法来加入值:

List<User> users = new List<User>();
users.Add(new User { Age = 23, Name = "sanity" });
users.Add(new User { Age = 24, Name = "Lin" });

3.0中的集合初始化器这样来构建一个list:

List<User> users = new List<User>
{
    new User { Age = 23, Name = "sanity" },
    new User { Age = 24, Name = "Lin" }
};

同样的反编译看下本质代码:

List<User> <>g__initLocal0 = new List<User>();
User <>g__initLocal1 = new User {
    Age = 0x17,
    Name = "sanity"
};
<>g__initLocal0.Add(<>g__initLocal1);
User <>g__initLocal2 = new User {
    Age = 0x18,
    Name = "Lin"
};
<>g__initLocal0.Add(<>g__initLocal2);
List<User> users = <>g__initLocal0;

同样的,从IL看出,编译器也为集合初始化器生成了临时变量,本质还是跟之前的相同。

 

Automatic Properties 自动属性

2.0方式:

public class User
{
    // Fields
    private int age;
    private string name;
 
    // Methods
    public User();
 
    // Properties
    public int Age { get; set; }
    public string Name { get; }
}
 
 

3.0:

public class User
{
    public int Age { get; private set; }
    public string Name { get; set; }
}

对于自动属性还有些规则:

1. 有附加逻辑的字段无法用自动属性实现,还是得需要传统的属性定义方式。

2. 必须同时实现get和set,对于只读或者只写属性,可为访问器添加private修饰符。

3. 还需要注意abstract property的自动属性实现。

 

Implicitly Typed Local Variables 隐式类型变量

Implicitly Typed Array 隐式类型数组

var i = 23;
var str = "Hello, Implicitly Type.";
var user = new User { Age = 23, Name = "Sanity" };
var users = new List<User>
{
    new User { Age = 23, Name = "sanity" },
    new User { Age = 24, Name = "Lin" }
};

看上述代码中,使用了javascript中到处可见的var声明变量,3.0也实现了var声明变量,这样意味着.NET变成了弱类型语言吗?Nope!

本质分析:

User <>g__initLocal0 = new User {
    Age = 0x17,
    Name = "Sanity"
};
User user = <>g__initLocal0;
List<User> <>g__initLocal1 = new List<User>();
User <>g__initLocal2 = new User {
    Age = 0x17,
    Name = "sanity"
};
<>g__initLocal1.Add(<>g__initLocal2);
User <>g__initLocal3 = new User {
    Age = 0x18,
    Name = "Lin"
};
<>g__initLocal1.Add(<>g__initLocal3);
List<User> users = <>g__initLocal1;

在IL中看出,其实编译器能够反推出变量本身的类型,我们只是通过var关键字将变量类型的声明托付给了编译器来完成。

对于var的几点规则:

1. 在.NET中,我们可以用object来声明任何类型变量,但这种声明方式存在着类型转换操作,而且如果变量为值类型的话,还会引起装箱,影响性能了。而var的实现,不存在这些现象;

2. 用var声明的变量必须进行初始化,而且不能为null,也不能类型不确定的表达式,否则编译器不能判断是什么类型;

3. 隐式类型还是强类型,是将类型声明交给编译器来完成了。

 

Anonymous Type

在隐式类型和对象初始化的帮助下,可以实现匿名类型了。

之前的User Class这样定义和使用:

public class User
{
    public int Age { get; set; }
    public string Name { get; set; }
}
 
var user = new User { Age = 23, Name = "Sanity" };

匿名类型让这一切变得更加简单:

var user = new { Age = 23, Name = "Sanity" };

匿名类型规则是:对象初始化器中的参数名称、类型和顺序都一致时,这些匿名类型的对象才是同一个类型。

 

Extension Methods扩展方法

扩展方法实现了在保持原有类型不做任何改变的情况下,对其进行扩展。实例:

static class MyExtensions
{
    public static void ShowInfo(this User user)
    {
        Console.WriteLine("Name: {0}\nAge: {1}", user.Name, user.Age);
    }
 
    public static void TellType(this User user)
    {
        Console.WriteLine("I am a user:");
        user.ShowInfo();
    }
}

调用实例方法一样来调用扩展方法:

static void Main(string[] args)
{
    var user = new User { Age = 23, Name = "Sanity" };
    user.ShowInfo();
    user.TellType();
    Console.ReadKey();
}

扩展方法规则:

1. 实例方法优先于扩展方法,命名空间内的扩展方法优先于命名空间外的。

2. 扩展方法可以继承;

3. 只支持对方法的扩展,不支持属性、事件。

4. 扩展方法是LINQ实现的基本条件;

5. 必须以this关键字标识扩展方法的第一个参数;

6. 扩展方法必须被实现为静态方法,被定义在非泛型静态类中。

 

参考资料:

1. Clr Via C#

2. C# in depth

3. 你必须知道的.NET