还是新开一篇吧,因为不光要记录各版本变化,还想把相关知识点衍生出来扩展一下,一天写不完就多几天,这里面知识点非常多。
C# 1.0,对应.NET 1.0/1.1,对应VS 2003,新特性:事件、委托。
C# 2.0,对应.NET 2.0,对应VS 2005,新特性:泛型(Dictionary、List)、匿名、迭代、可空、委托(协变/逆变)、部份类、静态类、属性访问控制
C# 3.0,对应.NET 3.0/3.5,对应VS 2008,新特性:隐式类型的部变量、对象集合初始化、查询表达式、表达式树、分部类和方法、AJAX、LINQ、Entity Framework、ADO.NET、类型推断(var)、自动属性、匿名类型、扩展方法、Lambda表达式
C# 4.0,对应.NET 4.0,对应VS 2010,新特性:dynamic、动态绑定、可选(默认)参数、命名参数、泛型的协变和逆变、互操作性
C# 5.0,对应.NET 4.5,对应VS 2012,新特性:异步编程(async/await)、调用方信息、带参数的泛型构造函数、支持null类型运算、case支持表达式、扩展属性
C# 6.0,对应.NET 4.6,对应VS 2015,新特性:主构造函数、using静态类、属性表达式、方法表达式、枚举参数、null判断、Constructor type parameter inference、内联out、自动属性增强、字符串嵌入值string.Format变成直接"{变量}"、nameof表达式、异常过滤器、catch和finally 中的 await 、无参数的结构体构造函数
一、C#1.0
1、事件、委托
事件就是一个特殊的委托,委托和事件就类似于字段和属性的关系,事件是对委托做了一个封装
事件必須用+=或-=(委託可以清空,事件不能清空)
我们可以将“委托”理解为“方法的抽象”,也就是说定义一个方法的模板,至于这个方法具体是怎么样的,就由方法自己去实现。
public delegate void SaySomething(string name); public event SaySomething Come; public void SayHello(string name) { Console.WriteLine("Hello," + name + "!"); } public void SayNiceToMeetYou(string name) { Console.WriteLine("Nice to meet you," + name + "!"); } public void test() { SaySomething sayhello = SayHello; SaySomething saynice = SayNiceToMeetYou; Come += sayhello; Come += saynice; Come("张三"); }
大概理解的是:委托相当于“定义”了一个规范(签名相同),由“继承”的具体方法来“实现”,注册事件相当于依次执行委托,也就是执行那些具体的方法。
因为在项目中没具体用过,所以实在难以理解,只好先这样吧。
二、C# 2.0
1、泛型
一次编码,多次使用、按需实例化、编译时就可以保证类型安全、不用做类型装换、获得一定的性能提升
泛型类、泛型方法、泛型委托、泛型接口
格式为<T>最常用的就是List<T>和Dictionary<T,T>,具体类型可以用的时候再定。
常用来和装箱/拆箱相比较,泛型可以提高性能(不用装拆箱),指定了类型,在编译时容易发现问题。
private static void DoSomething<T>(T a) { T b = a; Console.WriteLine(b); } static void Main(string[] args) { DoSomething("A"); DoSomething(1); Console.ReadKey(); }
可以看到,就把T当成是普通的Int或String看待就行,就看具体怎么用
泛型约束:
约束
|
描述
|
where T: struct
|
类型参数必须为值类型。
|
where T : class
|
类型参数必须为类型。
|
where T : new()
|
类型参数必须有一个公有、无参的构造函数。当于其它约束联合使用时,new()约束必须放在最后。
|
where T : <base class name>
|
类型参数必须是指定的基类型或是派生自指定的基类型。
|
where T : <interface name>
|
类型参数必须是指定的接口或是指定接口的实现。可以指定多个接口约束。接口约束也可以是泛型的。
|
语法就是 WHERE T : 约束,是用来约定这个泛型只能取什么值的
interface IMyInterface { } class Dictionary<TKey, TVal> where TKey : IComparable, IEnumerable where TVal : IMyInterface { public void Add(TKey key, TVal val) { } }
2、匿名方法
在使用委托时创建,本来委托要使用一个专门建的方法。但匿名方法可以直接写方法体(js不是天天这么用么。。。),以后有了lamda就更方便了
delegate void Del(int x); Del d = delegate(int k) { /* ... */ }; Thread t1 = new Thread (delegate() { Console.Write("Hello, "); } ); ThreadStart ts1 = new ThreadStart(Method1); //可以简写成下面这种 ThreadStart ts2 = Method1;
3、迭代器
迭代器的返回类型必须为 IEnumerable、IEnumerator、IEnumerable<T> 或 IEnumerator<T>。迭代器是使用在foreach中的集合
foreach不是for的简写,而是对实现了IEnumerable接口的集合进行遍历
而如果不想实现IEnumerator又想用foreach的话,可以使用yield return,相当于yield return帮我们实现了IEnumerator
迭代器代码使用 yield return 语句依次返回每个元素。yield break 将终止迭代
public class Persons : IEnumerable { public IEnumerator GetEnumerator() { yield return "1"; Thread.Sleep(1000); yield return "2"; Thread.Sleep(1000); yield return "3"; Thread.Sleep(1000); yield return "4"; Thread.Sleep(1000); yield return "5"; Thread.Sleep(1000); yield return "6"; } } class program { static void Main() { Persons arrPersons = new Persons(); foreach(string s in arrPersons){ Console.WriteLine(s); } Console.ReadLine(); } }
简单的说,迭代器就是foreach,前提条件是IEnumerator,如果没有IEnumerator,就用yield return。
4、可空类型
int? i = null; int j = i ?? 0;
可空类型经常配合数据库的可空字段使用。
int本来是不能赋null的,但用了int?,就可以赋null。
??是简写的一种运算符,相当于 int j = i==null? 0 : Convert.ToInt16(i);
注意,可空类型不能直接赋给值类型(因为有可能null),但反过来是可以的。
5、委托(协变/逆变)
协变针对委托的返回值,逆变针对参数; 逆变粗糙化,协变精细化
比较抽象,只了解了个大概
6、部份类、静态类、属性访问控制
部份类(partial):一个类长太了,分成两个类(可存放在不同地方),编译时视为同一个类
静态类(static):不用实例化就能直接访问,并长驻内存。静态类里只能有静态方法,但静态方法不一定要在静态类中。静态类/方法不能访问非静态类/方法,但非静态类/方法(即普通方法)无此限制。
属性访问控制:get和set前面可以加修饰符来控制访问权限,和类、方法的修饰符一致