国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
C#各版本新特性

泛型(Generics)

泛型是CLR 2.0中引入的最重要的新特性,使得可以在類、方法中對(duì)使用的類型進(jìn)行參數(shù)化。

例如,這里定義了一個(gè)泛型類:

 

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

 

使用的時(shí)候:

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

 

泛型的好處

  • 編譯時(shí)就可以保證類型安全
  • 不用做類型裝換,獲得一定的性能提升

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

除了泛型類之外,還有泛型方法、泛型委托、泛型接口:

//泛型委托 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)
可以給泛型的類型參數(shù)上加約束,可以要求這些類型參數(shù)滿足一定的條件

約束

說(shuō)明

where T: struct類型參數(shù)需是值類型
where T : class類型參數(shù)需是引用類型
where T : new()類型參數(shù)要有一個(gè)public的無(wú)參構(gòu)造函數(shù)
where T : <base class name>類型參數(shù)要派生自某個(gè)基類
where T : <interface name>類型參數(shù)要實(shí)現(xiàn)了某個(gè)接口
where T : U這里T和U都是類型參數(shù),T必須是或者派生自U

這些約束,可以同時(shí)一起使用:

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

 

default 關(guān)鍵字

這個(gè)關(guān)鍵可以使用在類型參數(shù)上:

default(T);

對(duì)于值類型,返回0,引用類型,返回null,對(duì)于結(jié)構(gòu)類型,會(huì)返回一個(gè)成員值全部為0的結(jié)構(gòu)實(shí)例。

迭代器(iterator)

可以在不實(shí)現(xiàn)IEnumerable就能使用foreach語(yǔ)句,在編譯器碰到y(tǒng)ield return時(shí),它會(huì)自動(dòng)生成IEnumerable 接口的方法。在實(shí)現(xiàn)迭代器的方法或?qū)傩灾?,返回類型必須是IEnumerable, IEnumerator, IEnumerable<T>,或 IEnumerator<T>。迭代器使得遍歷一些零碎數(shù)據(jù)的時(shí)候很方便,不用去實(shí)現(xiàn)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>,可空類型僅針對(duì)于值類型,不能針對(duì)引用類型去創(chuàng)建。System.Nullable<T>簡(jiǎn)寫為T ?。

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

如果HasValue為false,那么在使用value值的時(shí)候會(huì)拋出異常。把一個(gè)Nullable的變量x賦值給一個(gè)非Nullable的變量y可以這么寫:

int y = x ?? -1;

匿名方法(Anonymous Method)

在C#2.0之前,給只能用一個(gè)已經(jīng)申明好的方法去創(chuàng)建一個(gè)委托。有了匿名方法后,可以在創(chuàng)建委托的時(shí)候直接傳一個(gè)代碼塊過(guò)去。

delegate void Del(int x); Del d = delegate(int k) { /* ... */ }; System.Threading.Thread t1 = new System.Threading.Thread (delegate() { System.Console.Write("Hello, "); } ); 委托語(yǔ)法的簡(jiǎn)化// C# 1.0的寫法 ThreadStart ts1 = new ThreadStart(Method1); // C# 2.0可以這么寫 ThreadStart ts2 = Method1;

委托的協(xié)變和逆變(covariance and contravariance)

有下面的兩個(gè)類:

class Parent { } class Child: Parent { }

然后看下面的兩個(gè)委托:

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 是完全不同的類型,他們之間本身沒有任何的繼承關(guān)系,所以理論上來(lái)說(shuō)他們是不能相互賦值的。但是因?yàn)閰f(xié)變的關(guān)系,使得我們可以把DelgChild類型的委托賦值給DelgParent 類型的委托。協(xié)變針對(duì)委托的返回值,逆變針對(duì)參數(shù),原理是一樣的。

部分類(partial)

在申明一個(gè)類、結(jié)構(gòu)或者接口的時(shí)候,用partial關(guān)鍵字,可以讓源代碼分布在不同的文件中。我覺得這個(gè)東西完全是為了照顧Asp.net代碼分離而引入的功能,真沒什么太大的實(shí)際用處。微軟說(shuō)在一些大工程中可以把類分開在不同的文件中讓不同的人去實(shí)現(xiàn),方便團(tuán)隊(duì)協(xié)作,這個(gè)我覺得純屬胡扯。

部分類僅是編譯器提供的功能,在編譯的時(shí)候會(huì)把partial關(guān)鍵字定義的類和在一起去編譯,和CRL沒什么關(guān)系。

靜態(tài)類(static class)

靜態(tài)類就一個(gè)只能有靜態(tài)成員的類,用static關(guān)鍵字對(duì)類進(jìn)行標(biāo)示,靜態(tài)類不能被實(shí)例化。靜態(tài)類理論上相當(dāng)于一個(gè)只有靜態(tài)成員并且構(gòu)造函數(shù)為私有的普通類,靜態(tài)類相對(duì)來(lái)說(shuō)的好處就是,編譯器能夠保證靜態(tài)類不會(huì)添加任何非靜態(tài)成員。

global::

這個(gè)代表了全局命名空間(最上層的命名空間),也就是任何一個(gè)程序的默認(rèn)命名空間。

class TestApp { public class System { } const int Console = 7; static void Main() { //用這個(gè)訪問(wèn)就會(huì)出錯(cuò),System和Console都被占用了 //Console.WriteLine(number); global::System.Console.WriteLine(number); } }

extern alias

用來(lái)消除不同程序集中類名重復(fù)的沖突,這樣可以引用同一個(gè)程序集的不同版本,也就是說(shuō)在編譯的時(shí)候,提供了一個(gè)將有沖突的程序集進(jìn)行區(qū)分的手段。

在編譯的時(shí)候,使用命令行參數(shù)來(lái)指明alias,例如:

/r:aliasName=assembly1.dll

在Visual Studio里面,在被引用的程序集的屬性里面可以指定Alias的值,默認(rèn)是global。

然后在代碼里面就可以使用了:

extern alias aliasName; //這行需要在using這些語(yǔ)句的前面 using System; using System.Collections.Generic; using System.Text; using aliasName.XXX;

屬性Accessor訪問(wèn)控制

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

友元程序集(Friend Assembly)

可以讓其它程序集訪問(wèn)自己的internal成員(private的還是不行),使用Attributes來(lái)實(shí)現(xiàn),例如:

[assembly:InternalsVisibleTo("cs_friend_assemblies_2")]

注意這個(gè)作用范圍是整個(gè)程序集。

fixed關(guān)鍵字

可以使用fixed關(guān)鍵字來(lái)創(chuàng)建固定長(zhǎng)度的數(shù)組,但是數(shù)組只能是bool, byte, char, short, int, long, sbyte, ushort, uint, ulong, float, double中的一種。

這主要是為了更好的處理一些非托管的代碼。比如下面的這個(gè)結(jié)構(gòu)體:

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

如果不用fixed的話,無(wú)法預(yù)先占住128個(gè)char的空間,使用fixed后可以很好的和非托管代碼進(jìn)行交互。

volatile關(guān)鍵字

用來(lái)表示相關(guān)的字可能被多個(gè)線程同時(shí)訪問(wèn),編譯器不會(huì)對(duì)相應(yīng)的值做針對(duì)單線程下的優(yōu)化,保證相關(guān)的值在任何時(shí)候訪問(wèn)都是最新的。

#pragma warning

用來(lái)取消或者添加編譯時(shí)的警告信息。每個(gè)警告信息都會(huì)有個(gè)編號(hào),如果warning CS01016之類的,使用的時(shí)候取CS后面的那個(gè)數(shù)字,例如:

#pragma warning disable 414, 3021

這樣CS414和CS3021的警告信息就都不會(huì)顯示了。

C# 3.0

類型推斷

申明變量的時(shí)候,可以不用直指定類型:

var i = 5;var s = "Hello";//兩種寫法是一樣的int i = 5;string s = "Hello";

類型推斷也支持?jǐn)?shù)組:

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

擴(kuò)展方法

擴(kuò)展方法必須被定義在靜態(tài)類中,并且必須是非泛型、非嵌套的靜態(tài)類。例如:

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)    {        /**/    }}

上面一個(gè)是給string類型的對(duì)象添加了一個(gè)方法,另一個(gè)是給所有類型的數(shù)組添加了一個(gè)方法,方法有兩個(gè)整型參數(shù)。

擴(kuò)展方法只在當(dāng)前的命名空間類有效,如果所在命名空間被其它命名空間import引用了,那么在其它命名空間中也有效。擴(kuò)展方法的優(yōu)先級(jí)低于其它的常規(guī)方法,也就是說(shuō)如果擴(kuò)展方法與其它的方法相同,那么擴(kuò)展方法不會(huì)被調(diào)用。

Lamda表達(dá)式

可以看成是對(duì)匿名方法的一個(gè)語(yǔ)法上的簡(jiǎn)化,但是λ表達(dá)式同時(shí)可以裝換為表達(dá)式樹類型。

對(duì)象和集合的初始化

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;

自動(dòng)屬性

會(huì)自動(dòng)生成一個(gè)后臺(tái)的私有變量

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

查詢表達(dá)式

這個(gè)其實(shí)就是擴(kuò)展方法的運(yùn)用,編譯器提供了相關(guān)的語(yǔ)法便利,下面兩端代碼是等價(jià)的:

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

表達(dá)式樹

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

C# 4.0

協(xié)變和逆變

這個(gè)在C#2.0中就已經(jīng)支持委托的協(xié)變和逆變了,C#4.0開始支持針對(duì)泛型接口的協(xié)變和逆變:

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

協(xié)變和逆變僅針對(duì)引用類型。

動(dòng)態(tài)綁定

看例子:

class BaseClass{    public void print()    {        Console.WriteLine();    }}
Object o = new BaseClass();dynamic a = o;//這里可以調(diào)用print方法,在運(yùn)行時(shí)a會(huì)知道自己是個(gè)什么類型。 這里的缺點(diǎn)在于編譯的時(shí)候無(wú)法檢查方法的合法性,寫錯(cuò)的話就會(huì)出運(yùn)行時(shí)錯(cuò)誤。a.print();

可選參數(shù),命名參數(shù)

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

這樣,最后一個(gè)參數(shù)不給的話默認(rèn)值就是1,提供這個(gè)特性可以免去寫一些重載方法的麻煩。

調(diào)用方法的時(shí)候,可以指定參數(shù)的名字來(lái)給值,不用按照方法參數(shù)的順序來(lái)制定參數(shù)值:

CreateNewStudent(year:2, name:"Hima", studentid: 4); //沒有按照方法定義的參數(shù)順序

C# 5.0

1. 異步編程

在.Net 4.5中,通過(guò)async和await兩個(gè)關(guān)鍵字,引入了一種新的基于任務(wù)的異步編程模型(TAP)。在這種方式下,可以通過(guò)類似同步方式編寫異步代碼,極大簡(jiǎn)化了異步編程模型。如下式一個(gè)簡(jiǎn)單的實(shí)例:

    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);
    }

也許前面這個(gè)例子不足以體現(xiàn)async和await帶來(lái)的優(yōu)越性,下面這個(gè)例子就明顯多了:

    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);
        }
    }

關(guān)于基于任務(wù)的異步編程模型需要介紹的地方還比較多,不是一兩句能說(shuō)完的,有空的話后面再專門寫篇文章來(lái)詳細(xì)介紹下。另外也可參看微軟的官方網(wǎng)站:Visual Studio Asynchronous Programming,其官方文檔Task-Based Asynchronous Pattern Overview介紹的非常詳細(xì), VisualStudio中自帶的CSharp Language Specification中也有一些說(shuō)明。

2. 調(diào)用方信息

很多時(shí)候,我們需要在運(yùn)行過(guò)程中記錄一些調(diào)測(cè)的日志信息,如下所示:

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

為了調(diào)測(cè)方便,除了事件信息外,我們往往還需要知道發(fā)生該事件的代碼位置以及調(diào)用棧信息。在C++中,我們可以通過(guò)定義一個(gè)宏,然后再宏中通過(guò)__FILE__和__LINE__來(lái)獲取當(dāng)前代碼的位置,但C#并不支持宏,往往只能通過(guò)StackTrace來(lái)實(shí)現(xiàn)這一功能,但StackTrace卻有不是很靠譜,常常獲取不了我們所要的結(jié)果。

針對(duì)這個(gè)問(wèn)題,在.Net 4.5中引入了三個(gè)Attribute:CallerMemberName、CallerFilePath和CallerLineNumber。在編譯器的配合下,分別可以獲取到調(diào)用函數(shù)(準(zhǔn)確講應(yīng)該是成員)名稱,調(diào)用文件及調(diào)用行號(hào)。上面的TraceMessage函數(shù)可以實(shí)現(xiàn)如下:

    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);
    }

另外,在構(gòu)造函數(shù),析構(gòu)函數(shù)、屬性等特殊的地方調(diào)用CallerMemberName屬性所標(biāo)記的函數(shù)時(shí),獲取的值有所不同,其取值如下表所示:

 

調(diào)用的地方

CallerMemberName獲取的結(jié)果

方法、屬性或事件

方法,屬性或事件的名稱

構(gòu)造函數(shù)

字符串 ".ctor"

靜態(tài)構(gòu)造函數(shù)

字符串 ".cctor"

析構(gòu)函數(shù)

該字符串 "Finalize"

用戶定義的運(yùn)算符或轉(zhuǎn)換

生成的名稱成員,例如, "op_Addition"。

特性構(gòu)造函數(shù)

特性所應(yīng)用的成員的名稱

例如,對(duì)于在屬性中調(diào)用CallerMemberName所標(biāo)記的函數(shù)即可獲取屬性名稱,通過(guò)這種方式可以簡(jiǎn)化 INotifyPropertyChanged 接口的實(shí)現(xiàn)。

C# 6.0

1、自動(dòng)屬性的增強(qiáng)

1.1、自動(dòng)屬性初始化 (Initializers for auto-properties)

C#4.0下的果斷實(shí)現(xiàn)不了的。

C#6.0中自動(dòng)屬性的初始化方式

只要接觸過(guò)C#的肯定都會(huì)喜歡這種方式。真是簡(jiǎn)潔方便呀。

 

 1.2、只讀屬性初始化Getter-only auto-properties

先來(lái)看一下我們之前使用的方式吧

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

再來(lái)看一下C#6.0中

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

和第一條自動(dòng)屬性初始化使用方式一致。

2、Expression bodied function members

2.1 用Lambda作為函數(shù)體Expression bodies on method-like members

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

再來(lái)舉一個(gè)簡(jiǎn)單的例子:一個(gè)沒有返回值的函數(shù)

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

 

2.2、Lambda表達(dá)式用作屬性Expression bodies on property-like function members

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

現(xiàn)在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;    }

 

3、引用靜態(tài)類Using Static 

 在Using中可以指定一個(gè)靜態(tài)類,然后可以在隨后的代碼中直接使用靜態(tài)的成員

 

4、空值判斷Null-conditional operators  

 直接來(lái)看代碼和運(yùn)行結(jié)果

 通過(guò)結(jié)果可以發(fā)現(xiàn)返回的都為null,再也不像以前那樣繁瑣的判斷null勒。

 

5、字符串嵌入值    

在字符串中嵌入值

之前一直使用的方式是

現(xiàn)在我們可以簡(jiǎn)單的通過(guò)如下的方式進(jìn)行拼接

6、nameof表達(dá)式nameof expressions 

 在方法參數(shù)檢查時(shí),你可能經(jīng)??吹竭@樣的代碼(之前用的少,這次也算學(xué)到了)

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

里面有那個(gè)customer是我們手寫的字符串,在給customer改名時(shí),很容易把下面的那個(gè)字符串忘掉,C#6.0 nameof幫我們解決了這個(gè)問(wèn)題,看看新寫法

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

 

7、帶索引的對(duì)象初始化器Index initializers   

 直接通過(guò)索引進(jìn)行對(duì)象的初始化,原來(lái)真的可以實(shí)現(xiàn)

通過(guò)這種方式可以發(fā)現(xiàn)字典中只有三個(gè)元素,所以也就只有這三個(gè)索引可以訪問(wèn)額,其他類型的對(duì)象和集合也是可以通過(guò)這種方式進(jìn)行初始化的,在此就不進(jìn)行一一列舉了。

8、異常過(guò)濾器 (Exception filters)  

先來(lái)看一個(gè)移植過(guò)來(lái)的方法

            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");                }            }

在微軟的文檔中還給出了另一種用法,這個(gè)異常會(huì)在日志記錄失敗時(shí)拋給上一層調(diào)用者

        private static bool Log(Exception e)        {            ///處理一些日志            return false;        }         static void Main(string[] args)        {            try            {                ///            }            catch (Exception e){if (!Log(e))                {                }            }            Console.ReadLine();        }

 

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

 在C#5.0中,await關(guān)鍵字是不能出現(xiàn)在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.             } 

 

10、無(wú)參數(shù)的結(jié)構(gòu)體構(gòu)造函數(shù)—— Parameterless constructors in structs 

 

C# 7.0

1. out 變量(out variables)

  以前我們使用out變量必須在使用前進(jìn)行聲明,C# 7.0 給我們提供了一種更簡(jiǎn)潔的語(yǔ)法 “使用時(shí)進(jìn)行內(nèi)聯(lián)聲明” 。如下所示:

1   var input = ReadLine();2   if (int.TryParse(input, out var result))3   {4       WriteLine("您輸入的數(shù)字是:{0}",result);5   }6   else7   {8       WriteLine("無(wú)法解析輸入...");9   }

  上面代碼編譯后:

 1   int num; 2   string s = Console.ReadLine(); 3   if (int.TryParse(s, out num)) 4   { 5       Console.WriteLine("您輸入的數(shù)字是:{0}", num); 6   } 7   else 8   { 9       Console.WriteLine("無(wú)法解析輸入...");10   }

 原理解析:所謂的 “內(nèi)聯(lián)聲明” 編譯后就是以前的原始寫法,只是現(xiàn)在由編譯器來(lái)完成。

 備注:在進(jìn)行內(nèi)聯(lián)聲明時(shí),即可直接寫明變量的類型也可以寫隱式類型,因?yàn)閛ut關(guān)鍵字修飾的一定是局部變量。

2. 元組(Tuples)

 元組(Tuple)在 .Net 4.0 的時(shí)候就有了,但元組也有些缺點(diǎn),如:

   1)Tuple 會(huì)影響代碼的可讀性,因?yàn)樗膶傩悦际牵篒tem1,Item2.. 。

   2)Tuple 還不夠輕量級(jí),因?yàn)樗且妙愋停–lass)。

   備注:上述所指 Tuple 還不夠輕量級(jí),是從某種意義上來(lái)說(shuō)的或者是一種假設(shè),即假設(shè)分配操作非常的多。

 C# 7 中的元組(ValueTuple)解決了上述兩個(gè)缺點(diǎn):

   1)ValueTuple 支持語(yǔ)義上的字段命名。

   2)ValueTuple 是值類型(Struct)。

 1. 如何創(chuàng)建一個(gè)元組?

1   var tuple = (1, 2);                           // 使用語(yǔ)法糖創(chuàng)建元組2   var tuple2 = ValueTuple.Create(1, 2);         // 使用靜態(tài)方法【Create】創(chuàng)建元組3   var tuple3 = new ValueTuple<int, int>(1, 2);  // 使用 new 運(yùn)算符創(chuàng)建元組4   5   WriteLine($"first:{tuple.Item1}, second:{tuple.Item2}, 上面三種方式都是等價(jià)的。");

 原理解析:上面三種方式最終都是使用 new 運(yùn)算符來(lái)創(chuàng)建實(shí)例。

 2. 如何創(chuàng)建給字段命名的元組?

 1   // 左邊指定字段名稱 2   (int one, int two) tuple = (1, 2); 3   WriteLine($"first:{tuple.one}, second:{tuple.two}"); 4    5   // 右邊指定字段名稱 6   var tuple2 = (one: 1, two: 2); 7   WriteLine($"first:{tuple2.one}, second:{tuple2.two}"); 8    9   // 左右兩邊同時(shí)指定字段名稱10   (int one, int two) tuple3 = (first: 1, second: 2);    /* 此處會(huì)有警告:由于目標(biāo)類型(xx)已指定了其它名稱,因?yàn)楹雎栽M名稱xxx */11   WriteLine($"first:{tuple3.one}, second:{tuple3.two}");

 注:左右兩邊同時(shí)指定字段名稱,會(huì)使用左邊的字段名稱覆蓋右邊的字段名稱(一一對(duì)應(yīng))。 

 原理解析:上述給字段命名的元組在編譯后其字段名稱還是:Item1, Item2...,即:“命名”只是語(yǔ)義上的命名。

 3. 什么是解構(gòu)?

 解構(gòu)顧名思義就是將整體分解成部分。

 4. 解構(gòu)元組,如下所示:

1   var (one, two) = GetTuple();2   3   WriteLine($"first:{one}, second:{two}");
1   static (int, int) GetTuple()2   {3       return (1, 2);4   }

 原理解析:解構(gòu)元組就是將元組中的字段值賦值給聲明的局部變量(編譯后可查看)。

 備注:在解構(gòu)時(shí)“=”左邊能提取變量的數(shù)據(jù)類型(如上所示),元組中字段類型相同時(shí)即可提取具體類型也可以是隱式類型,但元組中字段類型

 不相同時(shí)只能提取隱式類型。

 5. 解構(gòu)可以應(yīng)用于 .Net 的任意類型,但需要編寫 Deconstruct 方法成員(實(shí)例或擴(kuò)展)。如下所示:

 1   public class Student 2   { 3       public Student(string name, int age) 4       { 5           Name = name; 6           Age = age; 7       } 8    9       public string Name { get; set; }10   11       public int Age { get; set; }12   13       public void Deconstruct(out string name, out int age)14       {15           name = Name;16           age = Age;17       }18   }

 使用方式如下:

1   var (Name, Age) = new Student("Mike", 30);2   3   WriteLine($"name:{Name}, age:{Age}");

 原理解析:編譯后就是由其實(shí)例調(diào)用 Deconstruct 方法,然后給局部變量賦值。

 Deconstruct 方法簽名:

1   // 實(shí)例簽名2   public void Deconstruct(out type variable1, out type variable2...)3   4   // 擴(kuò)展簽名5   public static void Deconstruct(this type instance, out type variable1, out type variable2...)

 總結(jié):1. 元組的原理是利用了成員類型的嵌套或者是說(shuō)成員類型的遞歸。2. 編譯器很牛B才能提供如此優(yōu)美的語(yǔ)法。

  使用 ValueTuple 則需要導(dǎo)入: Install - Package System.ValueTuple

3. 模式匹配(Pattern matching)

 1. is 表達(dá)式(is expressions),如:

 1   static int GetSum(IEnumerable<object> values) 2   { 3       var sum = 0; 4       if (values == null) return sum; 5    6       foreach (var item in values) 7       { 8           if (item is short)     // C# 7 之前的 is expressions 9           {10               sum += (short)item;11           }12           else if (item is int val)  // C# 7 的 is expressions13           {14               sum += val;15           }16           else if (item is string str && int.TryParse(str, out var result))  // is expressions 和 out variables 結(jié)合使用17           {18               sum += result;19           }20           else if (item is IEnumerable<object> subList)21           {22               sum += GetSum(subList);23           }24       }25   26       return sum;27   }

 使用方法:

1   條件控制語(yǔ)句(obj is type variable)2   {3      // Processing...4   }

 原理解析:此 is 非彼 is ,這個(gè)擴(kuò)展的 is 其實(shí)是 as 和 if 的組合。即它先進(jìn)行 as 轉(zhuǎn)換再進(jìn)行 if 判斷,判斷其結(jié)果是否為 null,不等于 null 則執(zhí)行

 語(yǔ)句塊邏輯,反之不行。由上可知其實(shí)C# 7之前我們也可實(shí)現(xiàn)類似的功能,只是寫法上比較繁瑣。

 2. switch語(yǔ)句更新(switch statement updates),如:

 1   static int GetSum(IEnumerable<object> values) 2   { 3       var sum = 0; 4       if (values == null) return 0; 5    6       foreach (var item in values) 7       { 8           switch (item) 9           {10               case 0:                // 常量模式匹配11                   break;12               case short sval:       // 類型模式匹配13                   sum += sval;14                   break;15               case int ival:16                   sum += ival;17                   break;18               case string str when int.TryParse(str, out var result):   // 類型模式匹配 + 條件表達(dá)式19                   sum += result;20                   break;21               case IEnumerable<object> subList when subList.Any():22                   sum += GetSum(subList);23                   break;24               default:25                   throw new InvalidOperationException("未知的類型");26           }27       }28   29       return sum;30   }

 使用方法:

 1   switch (item) 2   { 3       case type variable1: 4           // processing... 5           break; 6       case type variable2 when predicate: 7           // processing... 8           break; 9       default:10           // processing...11           break;12   }

 原理解析:此 switch 非彼 switch,編譯后你會(huì)發(fā)現(xiàn)擴(kuò)展的 switch 就是 as 、if 、goto 語(yǔ)句的組合體。同 is expressions 一樣,以前我們也能實(shí)

 現(xiàn)只是寫法比較繁瑣并且可讀性不強(qiáng)。

 總結(jié):模式匹配語(yǔ)法是想讓我們?cè)诤?jiǎn)單的情況下實(shí)現(xiàn)類似與多態(tài)一樣的動(dòng)態(tài)調(diào)用,即在運(yùn)行時(shí)確定成員類型和調(diào)用具體的實(shí)現(xiàn)。

4. 局部引用和引用返回 (Ref locals and returns)

 我們知道 C# 的 ref 和 out 關(guān)鍵字是對(duì)值傳遞的一個(gè)補(bǔ)充,是為了防止值類型大對(duì)象在Copy過(guò)程中損失更多的性能?,F(xiàn)在在C# 7中 ref 關(guān)鍵字得

 到了加強(qiáng),它不僅可以獲取值類型的引用而且還可以獲取某個(gè)變量(引用類型)的局部引用。如:

 1   static ref int GetLocalRef(int[,] arr, Func<int, bool> func) 2   { 3       for (int i = 0; i < arr.GetLength(0); i++) 4       { 5           for (int j = 0; j < arr.GetLength(1); j++) 6           { 7               if (func(arr[i, j])) 8               { 9                   return ref arr[i, j];10               }11           }12       }13   14       throw new InvalidOperationException("Not found");15   }

 Call:

1   int[,] arr = { { 10, 15 }, { 20, 25 } };2   ref var num = ref GetLocalRef(arr, c => c == 20);3   num = 600;4   5   Console.WriteLine(arr[1, 0]);

 Print results:

 

 使用方法:

 1. 方法的返回值必須是引用返回:

     a)  聲明方法簽名時(shí)必須在返回類型前加上 ref 修飾。

     b)  在每個(gè) return 關(guān)鍵字后也要加上 ref 修飾,以表明是返回引用。

 2. 分配引用(即賦值),必須在聲明局部變量前加上 ref 修飾,以及在方法返回引用前加上 ref 修飾。

 注:C# 開發(fā)的是托管代碼,所以一般不希望程序員去操作指針。并由上述可知在使用過(guò)程中需要大量的使用 ref 來(lái)標(biāo)明這是引用變量(編譯后其

 實(shí)沒那么多),當(dāng)然這也是為了提高代碼的可讀性。 

 總結(jié):雖然 C# 7 中提供了局部引用和引用返回,但為了防止濫用所以也有諸多約束,如:

 1. 你不能將一個(gè)值分配給 ref 變量,如:

1   ref int num = 10;   // error:無(wú)法使用值初始化按引用變量

 2. 你不能返回一個(gè)生存期不超過(guò)方法作用域的變量引用,如:

1 public ref int GetLocalRef(int num) => ref num;   // error: 無(wú)法按引用返回參數(shù),因?yàn)樗皇?ref 或 out 參數(shù)

 3. ref 不能修飾 “屬性” 和 “索引器”。 

1   var list = new List<int>();2   ref var n = ref list.Count;  // error: 屬性或索引器不能作為 out 或 ref 參數(shù)傳遞 

 原理解析:非常簡(jiǎn)單就是指針傳遞,并且個(gè)人覺得此語(yǔ)法的使用場(chǎng)景非常有限,都是用來(lái)處理大對(duì)象的,目的是減少GC提高性能。

5. 局部函數(shù)(Local functions)

 C# 7 中的一個(gè)功能“局部函數(shù)”,如下所示:

 1    static IEnumerable<char> GetCharList(string str) 2    { 3        if (IsNullOrWhiteSpace(str)) 4            throw new ArgumentNullException(nameof(str)); 5     6        return GetList(); 7     8        IEnumerable<char> GetList() 9        {10            for (int i = 0; i < str.Length; i++)11            {12                yield return str[i];13            }14        }15    }

 使用方法:

1   [數(shù)據(jù)類型,void] 方法名([參數(shù)])2   {3      // Method body;[] 里面都是可選項(xiàng)4   }

 原理解析:局部函數(shù)雖然是在其他函數(shù)內(nèi)部聲明,但它編譯后就是一個(gè)被 internal 修飾的靜態(tài)函數(shù),它是屬于類,至于它為什么能夠使用上級(jí)函

 數(shù)中的局部變量和參數(shù)呢?那是因?yàn)榫幾g器會(huì)根據(jù)其使用的成員生成一個(gè)新類型(Class/Struct)然后將其傳入函數(shù)中。由上可知?jiǎng)t局部函數(shù)的聲

 明跟位置無(wú)關(guān),并可無(wú)限嵌套。

 總結(jié):個(gè)人覺得局部函數(shù)是對(duì) C# 異常機(jī)制在語(yǔ)義上的一次補(bǔ)充(如上例),以及為代碼提供清晰的結(jié)構(gòu)而設(shè)置的語(yǔ)法。但局部函數(shù)也有其缺點(diǎn),

 就是局部函數(shù)中的代碼無(wú)法復(fù)用(反射除外)。

6. 更多的表達(dá)式體成員(More expression-bodied members)

 C# 6 的時(shí)候就支持表達(dá)式體成員,但當(dāng)時(shí)只支持“函數(shù)成員”和“只讀屬性”,這一特性在C# 7中得到了擴(kuò)展,它能支持更多的成員:構(gòu)造函數(shù)

 、析構(gòu)函數(shù)、帶 get,set 訪問(wèn)器的屬性、以及索引器。如下所示:

 1   public class Student 2   { 3       private string _name; 4    5       // Expression-bodied constructor 6       public Student(string name) => _name = name; 7    8       // Expression-bodied finalizer 9       ~Student() => Console.WriteLine("Finalized!");10   11       // Expression-bodied get / set accessors.12       public string Name13       {14           get => _name;15           set => _name = value ?? "Mike";16       }17   18       // Expression-bodied indexers19       public string this[string name] => Convert.ToBase64String(Encoding.UTF8.GetBytes(name));20   }

 備注:索引器其實(shí)在C# 6中就得到了支持,但其它三種在C# 6中未得到支持。

7. Throw 表達(dá)式(Throw expressions) 

 異常機(jī)制是C#的重要組成部分,但在以前并不是所有語(yǔ)句都可以拋出異常的,如:條件表達(dá)式(? :)、null合并運(yùn)算符(??)、一些Lambda

 表達(dá)式。而使用 C# 7 您可在任意地方拋出異常。如:

 1   public class Student 2   { 3       private string _name = GetName() ?? throw new ArgumentNullException(nameof(GetName)); 4    5       private int _age; 6    7       public int Age 8       { 9           get => _age;10           set => _age = value <= 0 || value >= 130 ? throw new ArgumentException("參數(shù)不合法") : value;11       }12   13       static string GetName() => null;14   }

8. 擴(kuò)展異步返回類型(Generalized async return types) 

 以前異步的返回類型必須是:Task、Task<T>、void,現(xiàn)在 C# 7 中新增了一種類型:ValueTask<T>,如下所示:

1   public async ValueTask<int> Func()2   {3       await Task.Delay(3000);4       return 100;5   }

 總結(jié):ValueTask<T> 與 ValueTuple 非常相似,所以就不列舉: ValueTask<T> 與 Task 之間的異同了,但它們都是為了優(yōu)化特定場(chǎng)景性能而

 新增的類型。

  使用 ValueTask<T> 則需要導(dǎo)入: Install - Package System.Threading.Tasks.Extensions

9. 數(shù)字文本語(yǔ)法的改進(jìn)(Numeric literal syntax improvements) 

 C# 7 還包含兩個(gè)新特性:二進(jìn)制文字、數(shù)字分隔符,如下所示:

1   var one = 0b0001;2   var sixteen = 0b0001_0000;3   4   long salary = 1000_000_000;5   decimal pi = 3.141_592_653_589m;

 注:二進(jìn)制文本是以0b(零b)開頭,字母不區(qū)分大小寫;數(shù)字分隔符只有三個(gè)地方不能寫:開頭,結(jié)尾,小數(shù)點(diǎn)前后。

 總結(jié):二進(jìn)制文本,數(shù)字分隔符 可使常量值更具可讀性。

 

2-6 處處: http://www.cnblogs.com/zq20/p/6323205.html
7 出處:http://www.cnblogs.com/VVStudy/ 

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
C#高效編程話題集2(每期10話題) - 不如來(lái)編碼-luminji's web - 博客園
C#編程 | 那些C#中很少人知道的科技
帶你了解C#每個(gè)版本新特性
基本的C#面試問(wèn)題(15例)
Linq基本語(yǔ)法
.Net 面試題整理(一)
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服