C#各版本新特性

html

C# 2.0web

泛型(Generics)express

泛型是CLR 2.0中引入的最重要的新特性,使得能够在类、方法中对使用的类型进行参数化。编程

例如,这里定义了一个泛型类:数组

 

class MyCollection<T> 
{
 T variable1; private void Add(T param)
{ } 
} 

 

使用的时候:安全

MyCollection<string> list2 = new MyCollection<string>();
MyCollection<Object> list3 = new MyCollection<Object>();

 

泛型的好处app

  • 编译时就能够保证类型安全
  • 不用作类型装换,得到必定的性能提高

泛型方法、泛型委托、泛型接口less

除了泛型类以外,还有泛型方法、泛型委托、泛型接口:异步

 
//泛型委托 
public static delegate T1 MyDelegate<T1, T2>(T2 item); 
MyDelegate<Int32, String> MyFunc = new MyDelegate<Int32, String>(SomeMethd); //泛型接口 public class MyClass<T1, T2, T3> : MyInteface<T1, T2, T3> { public T1 Method1(T2 param1, T3 param2) { throw new NotImplementedException(); } } interface MyInteface<T1, T2, T3> { T1 Method1(T2 param1, T3 param2); } //泛型方法 static void Swap<T>(ref T t1, ref T t2) { T temp = t1; t1 = t2; t2 = temp; }
String str1
= "a"; String str2 = "b"; Swap<String>(ref str1, ref str2);

 

泛型约束(constraints)
能够给泛型的类型参数上加约束,能够要求这些类型参数知足必定的条件

约束async

说明

where T: struct 类型参数需是值类型
where T : class 类型参数需是引用类型
where T : new() 类型参数要有一个public的无参构造函数
where T : <base class name> 类型参数要派生自某个基类
where T : <interface name> 类型参数要实现了某个接口
where T : U 这里T和U都是类型参数,T必须是或者派生自U

这些约束,能够同时一块儿使用:

class EmployeeList<T> where T : Employee, IEmployee, System.IComparable<T>, new() 
{
  // ...
}

 

default 关键字

这个关键可使用在类型参数上:

default(T);

对于值类型,返回0,引用类型,返回null,对于结构类型,会返回一个成员值所有为0的结构实例。

迭代器(iterator)

能够在不实现IEnumerable就能使用foreach语句,在编译器碰到yield return时,它会自动生成IEnumerable 接口的方法。在实现迭代器的方法或属性中,返回类型必须是IEnumerable, IEnumerator, IEnumerable<T>,或 IEnumerator<T>。迭代器使得遍历一些零碎数据的时候很方便,不用去实现Current, MoveNext 这些方法。

public System.Collections.IEnumerator GetEnumerator() { yield return -1; for (int i = 1; i < max; i++) { yield return i; } }

可空类型(Nullable Type)

可空类型System.Nullable<T>,可空类型仅针对于值类型,不能针对引用类型去建立。System.Nullable<T>简写为T ?。

int? num = null; if (num.HasValue == true) { System.Console.WriteLine("num = " + num.Value); } else { System.Console.WriteLine("num = Null"); }

若是HasValue为false,那么在使用value值的时候会抛出异常。把一个Nullable的变量x赋值给一个非Nullable的变量y能够这么写:

int y = x ?? -1;

匿名方法(Anonymous Method)

在C#2.0以前,给只能用一个已经申明好的方法去建立一个委托。有了匿名方法后,能够在建立委托的时候直接传一个代码块过去。

delegate void Del(int x); Del d = delegate(int k) { /* ... */ }; System.Threading.Thread t1 = new System.Threading.Thread (delegate() { System.Console.Write("Hello, "); } ); 委托语法的简化// C# 1.0的写法 ThreadStart ts1 = new ThreadStart(Method1); // C# 2.0能够这么写 ThreadStart ts2 = Method1;

 

委托的协变和逆变(covariance and contravariance)

有下面的两个类:

class Parent { } class Child: Parent { }

而后看下面的两个委托:

public delegate Parent DelgParent(); 

public delegate Child DelgChild(); 

public static Parent Method1() { return null; } 

public static Child Method2() { return null; } 

static void Main() { DelgParent del1= Method1; DelgChild del2= Method2; del1 = del2; }

注意上面的,DelgParent 和DelgChild 是彻底不一样的类型,他们之间自己没有任何的继承关系,因此理论上来讲他们是不能相互赋值的。可是由于协变的关系,使得咱们能够把DelgChild类型的委托赋值给DelgParent 类型的委托。协变针对委托的返回值,逆变针对参数,原理是同样的。

部分类(partial)

在申明一个类、结构或者接口的时候,用partial关键字,可让源代码分布在不一样的文件中。我以为这个东西彻底是为了照顾Asp.net代码分离而引入的功能,真没什么太大的实际用处。微软说在一些大工程中能够把类分开在不一样的文件中让不一样的人去实现,方便团队协做,这个我以为纯属胡扯。

部分类仅是编译器提供的功能,在编译的时候会把partial关键字定义的类和在一块儿去编译,和CRL没什么关系。

静态类(static class)

静态类就一个只能有静态成员的类,用static关键字对类进行标示,静态类不能被实例化。静态类理论上至关于一个只有静态成员而且构造函数为私有的普通类,静态类相对来讲的好处就是,编译器可以保证静态类不会添加任何非静态成员。

global::

这个表明了全局命名空间(最上层的命名空间),也就是任何一个程序的默认命名空间。

class TestApp { public class System { } const int Console = 7; static void Main() { //用这个访问就会出错,System和Console都被占用了 //Console.WriteLine(number); global::System.Console.WriteLine(number); } }

extern alias

用来消除不一样程序集中类名重复的冲突,这样能够引用同一个程序集的不一样版本,也就是说在编译的时候,提供了一个将有冲突的程序集进行区分的手段。

在编译的时候,使用命令行参数来指明alias,例如:

/r:aliasName=assembly1.dll

在Visual Studio里面,在被引用的程序集的属性里面能够指定Alias的值,默认是global。

而后在代码里面就可使用了:

extern alias aliasName; //这行须要在using这些语句的前面 using System; using System.Collections.Generic; using System.Text; using aliasName.XXX;

属性Accessor访问控制

public virtual int TestProperty { protected set { } get { return 0; } }

友元程序集(Friend Assembly)

可让其它程序集访问本身的internal成员(private的仍是不行),使用Attributes来实现,例如:

[assembly:InternalsVisibleTo("cs_friend_assemblies_2")]

注意这个做用范围是整个程序集。

fixed关键字

可使用fixed关键字来建立固定长度的数组,可是数组只能是bool, byte, char, short, int, long, sbyte, ushort, uint, ulong, float, double中的一种。

这主要是为了更好的处理一些非托管的代码。好比下面的这个结构体:

public struct MyArray { public fixed char pathName[128]; }

若是不用fixed的话,没法预先占住128个char的空间,使用fixed后能够很好的和非托管代码进行交互。

volatile关键字

用来表示相关的字可能被多个线程同时访问,编译器不会对相应的值作针对单线程下的优化,保证相关的值在任什么时候候访问都是最新的。

#pragma warning

用来取消或者添加编译时的警告信息。每一个警告信息都会有个编号,若是warning CS01016之类的,使用的时候取CS后面的那个数字,例如:

#pragma warning disable 414, 3021

这样CS414和CS3021的警告信息就都不会显示了。

C# 3.0

类型推断

申明变量的时候,能够不用直指定类型:

var i = 5;
var s = "Hello";
//两种写法是同样的 int i = 5; string s = "Hello";

类型推断也支持数组:

var b = new[] { 1, 1.5, 2, 2.5 };            // double[] var c = new[] { "hello", null, "world” }; // string[]

扩展方法

扩展方法必须被定义在静态类中,而且必须是非泛型、非嵌套的静态类。例如:

public static class JeffClass { public static int StrToInt32(this string s) { return Int32.Parse(s); } public static T[] SomeMethd<T>(this T[] source, int pram1, int pram2) { /**/ } }

上面一个是给string类型的对象添加了一个方法,另外一个是给全部类型的数组添加了一个方法,方法有两个整型参数。

扩展方法只在当前的命名空间类有效,若是所在命名空间被其它命名空间import引用了,那么在其它命名空间中也有效。扩展方法的优先级低于其它的常规方法,也就是说若是扩展方法与其它的方法相同,那么扩展方法不会被调用。

Lamda表达式

能够当作是对匿名方法的一个语法上的简化,可是λ表达式同时能够装换为表达式树类型。

对象和集合的初始化

var contacts = new List<Contact> {
   new Contact { Name = "Chris", PhoneNumbers = { "123455", "6688" } }, new Contact { Name = "Jeffrey", PhoneNumbers = { "112233" } } };

匿名类型

var p1 = new { Name = "Lawnmower", Price = 495.00 }; var p2 = new { Name = "Shovel", Price = 26.95 }; p1 = p2;

自动属性

会自动生成一个后台的私有变量

public Class Point
{
   public int X { get; set; } public int Y { get; set; } }

查询表达式

这个其实就是扩展方法的运用,编译器提供了相关的语法便利,下面两端代码是等价的:

from g in
   from c in customers group c by c.Country select new { Country = g.Key, CustCount = g.Count() } customers. GroupBy(c => c.Country). Select(g => new { Country = g.Key, CustCount = g.Count() })

表达式树

Func<int,int> f = x => x + 1; Expression<Func<int,int>> e = x => x + 1; 

C# 4.0

协变和逆变

这个在C#2.0中就已经支持委托的协变和逆变了,C#4.0开始支持针对泛型接口的协变和逆变:

IList<string> strings = new List<string>(); IList<object> objects = strings;

协变和逆变仅针对引用类型。

动态绑定

看例子:

class BaseClass
{
    public void print() { Console.WriteLine(); } }
Object o = new BaseClass();
dynamic a = o;
//这里能够调用print方法,在运行时a会知道本身是个什么类型。 这里的缺点在于编译的时候没法检查方法的合法性,写错的话就会出运行时错误。 a.print();

可选参数,命名参数

private void CreateNewStudent(string name, int studentid = 0, int year = 1)

这样,最后一个参数不给的话默认值就是1,提供这个特性能够免去写一些重载方法的麻烦。

调用方法的时候,能够指定参数的名字来给值,不用按照方法参数的顺序来制定参数值:

CreateNewStudent(year:2, name:"Hima", studentid: 4); //没有按照方法定义的参数顺序
C# 5.0 

1. 异步编程

在.Net 4.5中,经过async和await两个关键字,引入了一种新的基于任务的异步编程模型(TAP)。在这种方式下,能够经过相似同步方式编写异步代码,极大简化了异步编程模型。以下式一个简单的实例:

    static async void DownloadStringAsync2(Uri uri)
    {
        var webClient = new WebClient();
        var result = await webClient.DownloadStringTaskAsync(uri);
        Console.WriteLine(result);
    }

而以前的方式是这样的:

    static void DownloadStringAsync(Uri uri)
    {
        var webClient = new WebClient();
        webClient.DownloadStringCompleted += (s, e) =>
            {
                Console.WriteLine(e.Result);
            };
        webClient.DownloadStringAsync(uri);
    }

也许前面这个例子不足以体现async和await带来的优越性,下面这个例子就明显多了:

    public void CopyToAsyncTheHardWay(Stream source, Stream destination)
    {
        byte[] buffer = new byte[0x1000];
        Action<IAsyncResult> readWriteLoop = null;
        readWriteLoop = iar =>
        {
            for (bool isRead = (iar == null); ; isRead = !isRead)
            {
                switch (isRead)
                {
                    case true:
                        iar = source.BeginRead(buffer, 0, buffer.Length,
                            readResult =>
                            {
                                if (readResult.CompletedSynchronously) return;
                                readWriteLoop(readResult);
                            }, null);
                        if (!iar.CompletedSynchronously) return;
                        break;
                    case false:
                        int numRead = source.EndRead(iar);
                        if (numRead == 0)
                        {
                            return;
                        }
                        iar = destination.BeginWrite(buffer, 0, numRead,
                            writeResult =>
                            {
                                if (writeResult.CompletedSynchronously) return;
                                destination.EndWrite(writeResult);
                                readWriteLoop(null);
                            }, null);
                        if (!iar.CompletedSynchronously) return;
                        destination.EndWrite(iar);
                        break;
                }
            }
        };
        readWriteLoop(null);
    }

    public async Task CopyToAsync(Stream source, Stream destination)
    {
        byte[] buffer = new byte[0x1000];
        int numRead;
        while ((numRead = await source.ReadAsync(buffer, 0, buffer.Length)) != 0)
        {
            await destination.WriteAsync(buffer, 0, numRead);
        }
    }

关于基于任务的异步编程模型须要介绍的地方还比较多,不是一两句能说完的,有空的话后面再专门写篇文章来详细介绍下。另外也可参看微软的官方网站:Visual Studio Asynchronous Programming,其官方文档Task-Based Asynchronous Pattern Overview介绍的很是详细, VisualStudio中自带的CSharp Language Specification中也有一些说明。

2. 调用方信息

不少时候,咱们须要在运行过程当中记录一些调测的日志信息,以下所示:

    public void DoProcessing()
    {
        TraceMessage("Something happened.");
    }

为了调测方便,除了事件信息外,咱们每每还须要知道发生该事件的代码位置以及调用栈信息。在C++中,咱们能够经过定义一个宏,而后再宏中经过__FILE__和__LINE__来获取当前代码的位置,但C#并不支持宏,每每只能经过StackTrace来实现这一功能,但StackTrace却有不是很靠谱,经常获取不了咱们所要的结果。

针对这个问题,在.Net 4.5中引入了三个Attribute:CallerMemberName、CallerFilePath和CallerLineNumber。在编译器的配合下,分别能够获取到调用函数(准确讲应该是成员)名称,调用文件及调用行号。上面的TraceMessage函数能够实现以下:

    public void TraceMessage(string message,
            [CallerMemberName] string memberName = "",
            [CallerFilePath] string sourceFilePath = "",
            [CallerLineNumber] int sourceLineNumber = 0)
    {
        Trace.WriteLine("message: " + message);
        Trace.WriteLine("member name: " + memberName);
        Trace.WriteLine("source file path: " + sourceFilePath);
        Trace.WriteLine("source line number: " + sourceLineNumber);
    }

另外,在构造函数,析构函数、属性等特殊的地方调用CallerMemberName属性所标记的函数时,获取的值有所不一样,其取值以下表所示:

调用的地方

CallerMemberName获取的结果

方法、属性或事件

方法,属性或事件的名称

构造函数

字符串 ".ctor"

静态构造函数

字符串 ".cctor"

析构函数

该字符串 "Finalize"

用户定义的运算符或转换

生成的名称成员,例如, "op_Addition"。

特性构造函数

特性所应用的成员的名称

例如,对于在属性中调用CallerMemberName所标记的函数便可获取属性名称,经过这种方式能够简化 INotifyPropertyChanged 接口的实现。

C# 6.0 

一、自动属性的加强

1.一、自动属性初始化 (Initializers for auto-properties)

C#4.0下的果断实现不了的。

C#6.0中自动属性的初始化方式

只要接触过C#的确定都会喜欢这种方式。真是简洁方便呀。

 

 1.二、只读属性初始化Getter-only auto-properties

先来看一下咱们以前使用的方式吧

    public class Customer
    {
        public string Name { get; }

        public Customer(string firstName,string lastName)
        {
            Name = firstName +" "+ lastName;
        }
    }

再来看一下C#6.0中

    public class Customer
    {
        public string FirstName { get; }="aehyok";
        public string LastName { get; }="Kris";

    }

和第一条自动属性初始化使用方式一致。

二、Expression bodied function members

2.1 用Lambda做为函数体Expression bodies on method-like members

public Point Move(int dx, int dy) => new Point(x + dx, y + dy);  

再来举一个简单的例子:一个没有返回值的函数

public void Print() => Console.WriteLine(FirstName + " " + LastName);

 

2.二、Lambda表达式用做属性Expression bodies on property-like function members

        public override string ToString()
        {
            return FirstName + " " + LastName;
        }

如今C#6中

    public class User
    {
        public string FirstName { get; set; }

        public string LastName { get; set; }

        public override string ToString() => string.Format("{0}——{1}", FirstName, LastName);

        public string FullName => FirstName + " " + LastName;
    }

 

三、引用静态类Using Static 

 在Using中能够指定一个静态类,而后能够在随后的代码中直接使用静态的成员

 

四、空值判断Null-conditional operators  

 直接来看代码和运行结果

 经过结果能够发现返回的都为null,不再像之前那样繁琐的判断null勒。

 

五、字符串嵌入值    

在字符串中嵌入值

以前一直使用的方式是

如今咱们能够简单的经过以下的方式进行拼接

六、nameof表达式nameof expressions 

 在方法参数检查时,你可能常常看到这样的代码(以前用的少,此次也算学到了)

        public static void AddCustomer(Customer customer)
        {
            if (customer == null)
            {
                throw new ArgumentNullException("customer");
            }
        }

里面有那个customer是咱们手写的字符串,在给customer更名时,很容易把下面的那个字符串忘掉,C#6.0 nameof帮咱们解决了这个问题,看看新写法

        public static void AddCustomer(Customer customer)
        {
            if (customer == null)
            {
                throw new ArgumentNullException(nameof(customer));
            }
        }

 

七、带索引的对象初始化器Index initializers   

 直接经过索引进行对象的初始化,原来真的能够实现

经过这种方式能够发现字典中只有三个元素,因此也就只有这三个索引能够访问额,其余类型的对象和集合也是能够经过这种方式进行初始化的,在此就不进行一一列举了。

八、异常过滤器 (Exception filters)  

先来看一个移植过来的方法

            try
            {
                var numbers = new Dictionary<int, string> {[7] = "seven",[9] = "nine",[13] = "thirteen" };
            }
            catch (ArgumentNullException e)
            {
                if (e.ParamName == "customer")
                {
                    Console.WriteLine("customer can not be null");
                }
            }

在微软的文档中还给出了另外一种用法,这个异常会在日志记录失败时抛给上一层调用者

        private static bool Log(Exception e)
        {
            ///处理一些日志
            return false;
        } 

        static void Main(string[] args)
        {

            try
            {
                ///
            }
            catch (Exception e){if (!Log(e))
                {

                }
            }

            Console.ReadLine();
        }

 

九、catch和finally 中的 await —— Await in catch and finally blocks

 在C#5.0中,await关键字是不能出如今catch和finnaly块中的。而在6.0中

            try
            {
                res = await Resource.OpenAsync(…); // You could do this. … 
            }
            catch (ResourceException e)
            {
                await Resource.LogAsync(res, e); // Now you can do this … 
            } finally
            {
                if (res != null)
                    await res.CloseAsync(); // … and this. 
            } 

 

十、无参数的结构体构造函数—— Parameterless constructors in structs 

 

相关文章
相关标签/搜索