上學(xué)時(shí)學(xué)習C#和.NET,當時(shí)網(wǎng)上的資源不像現在這樣豐富,所以去電腦城買(mǎi)了張盜版的VS2005的光盤(pán),安裝時(shí)才發(fā)現是VS2003,當時(shí)有一種被坑的感覺(jué),但也正是如此,讓我有了一個(gè)完整的.NET的學(xué)習生涯。
一直都認為學(xué)習語(yǔ)言應該系統的進(jìn)行學(xué)習,了解每一個(gè)版本的新增特性,才能在實(shí)際應用中做到有的放矢。最近發(fā)現團隊中有不少人雖然用著(zhù)最新的技術(shù),但知識儲備還停留在一個(gè)比較初始的狀態(tài),這樣在編碼過(guò)程中會(huì )走不少彎路。
本文梳理下C#從1.0到7.0版本的一些常用特性,對于不常用的或者我沒(méi)有用到過(guò)的一些特性,會(huì )列出來(lái),但不會(huì )做詳細描述。另外C#8.0現在還沒(méi)有正式推出,并且目前我們也只是在使用dotNet Core2.1,所以C#8.0本文也不會(huì )涉及。
| C# | VS版本 | CLR版本 | .NET Framework |
|---|---|---|---|
| 1.0 | VS2002 | 1.0 | 1.0 |
| 1.1 | VS2003 | 1.1 | 1.1 |
在C#1.0或1.1版本中,從語(yǔ)言的角度就是基本的面向對象的語(yǔ)法,可以說(shuō)任何一本C#語(yǔ)言的書(shū)籍都包含了C#1.X的所有內容。
如果您已經(jīng)在使用C#語(yǔ)言編寫(xiě)代碼,那么C#1.X的相關(guān)知識應該已經(jīng)掌握?;A語(yǔ)法部分這里就不再贅述了。
| C# | VS版本 | CLR版本 | .NET Framework |
|---|---|---|---|
| 2.0 | VS2005 | 2.0 | 2.0 |
2.0中對應VS2005我用的也不多,因為很快就被VS2008替代了,不過(guò)在語(yǔ)言方面卻帶來(lái)了很多新的東西。
C#2中最重要的一個(gè)特性應該就是泛型。泛型的用處就是在一些場(chǎng)景下可以減少強制轉換來(lái)提高性能。在C#1中就有很多的強制轉換,特別是對一些集合進(jìn)行遍歷時(shí),如ArrayList、HashTable,因為他們是為不同數據類(lèi)型設計的集合,所以他們中鍵和值的類(lèi)型都是object,這就意味著(zhù)會(huì )平凡發(fā)生裝箱拆箱的操作。C#2中有了泛型,所以我們可以使用List
.NET已經(jīng)通過(guò)了很多的泛型類(lèi)型供我們使用,如上面提到的List
分部類(lèi)可以允許我們在多個(gè)文件中為一個(gè)類(lèi)型(class、struct、interface)編寫(xiě)代碼,在A(yíng)sp.Net2.0中用的極為廣泛。新建一個(gè)Aspx頁(yè)面,頁(yè)面的CodeBehind和頁(yè)面中的控件的定義就是通過(guò)分部類(lèi)來(lái)實(shí)現的。如下:
public partial class _Default : System.Web.UI.Page
public partial class _Default
分部類(lèi)使用關(guān)鍵字partial來(lái)定義,當一個(gè)類(lèi)中的代碼非常多時(shí),可以使用分部類(lèi)來(lái)進(jìn)行拆分,這對代碼的閱讀很有好處,而且不會(huì )影響調用。不過(guò)現在我們前后端分離,后端代碼要做到單一職責原則,不會(huì )有很多大的類(lèi),所以這個(gè)特性很少用到。
靜態(tài)類(lèi)中的公用方法必須也是靜態(tài)的,可以由類(lèi)名直接調用,不需要實(shí)例化,比較適用于編寫(xiě)一些工具類(lèi)。如System.Math類(lèi)就是靜態(tài)類(lèi)。工具類(lèi)有一些特點(diǎn),如:所有成員都是靜態(tài)的、不需要被繼承、不需要進(jìn)行實(shí)例化。在C#1中我們可以通過(guò)如下代碼來(lái)實(shí)現:
//聲明為密封類(lèi)防止被繼承
public sealed class StringHelper
{
//添加私有無(wú)參構造函ˉ數防止被實(shí)例化,如果不添加私有構造函數
//會(huì )自動(dòng)生成共有無(wú)參構造函數
private StringHelper(){};
public static int StringToInt32(string input)
{
int result=0;
Int32.TryParse(input, out result);
return result;
}
}
C#2中可以使用靜態(tài)類(lèi)來(lái)實(shí)現:
public static class StringHelper
{
public static int StringToInt32(string input)
{
int result=0;
Int32.TryParse(input, out result);
return result;
}
}
在C#1中聲明屬性,屬性中的get和set的訪(fǎng)問(wèn)級別是和屬性一致,要么都是public要么都是private,如果要實(shí)現get和set有不同的訪(fǎng)問(wèn)級別,則需要用一種變通的方式,自己寫(xiě)GetXXX和SetXXX方法。在C#2中可以單獨設置get和set的訪(fǎng)問(wèn)級別,如下:
private string _name;
public string Name
{
get { return _name; }
private set { _name = value; }
}
需要注意的是,不能講屬性設置為私有的,而將其中的get或是set設置成公有的,也不能給set和get設置相同的訪(fǎng)問(wèn)級別,當set和get的訪(fǎng)問(wèn)級別相同時(shí),我們可以直接設置在屬性上。
命名空間可以用來(lái)組織類(lèi),當不同的命名空間中有相同的類(lèi)時(shí),可以使用完全限定名來(lái)防止類(lèi)名的沖突,C#1中可以使用空間別名來(lái)簡(jiǎn)化書(shū)寫(xiě),空間別名用using關(guān)鍵字實(shí)現。但還有一些特殊情況,使用using并不能完全解決,所以C#2中提供了下面幾種特性:
我們在構建命名空間和類(lèi)的時(shí)候,盡量避免出現沖突的情況,這個(gè)特性也較少用到。
當我們希望一個(gè)程序集中的類(lèi)型可以被外部的某些程序集訪(fǎng)問(wèn),這時(shí)如果設置成Public,就可以被所有的外部程序集訪(fǎng)問(wèn)。怎樣只讓部分程序集訪(fǎng)問(wèn),就要使用友元程序集了,具體參考之前的博文《C#:友元程序集(http://blog.fwhyy.com/2010/11/csharp-a-friend-assembly/)》
可空類(lèi)型就是允許值類(lèi)型的值為null。通常值類(lèi)型的值是不應該為null的,但我們很多應用是和數據庫打交道的,而數據庫中的類(lèi)型都是可以為null值的,這就造成了我們寫(xiě)程序的時(shí)候有時(shí)需要將值類(lèi)型設置為null。在C#1中通常使用”魔值“來(lái)處理這種情況,比如DateTiem.MinValue、Int32.MinValue。在A(yíng)DO.NET中所有類(lèi)型的空值可以用DBNull.Value來(lái)表示。C#2中可空類(lèi)型主要是使用System.Nullable
Nullable<int> i = 20;
Nullable<bool> b = true;
C#2中也提供了更方便的定義方式,使用操作符?:
int? i = 20;
bool? b = true;
C#2中對迭代器提供了更便捷的實(shí)現方式。提到迭代器,有兩個(gè)概念需要了解
看下面一個(gè)例子:
public class Test
{
static void Main()
{
Person arrPerson = new Person("oec2003","oec2004","oec2005");
foreach (string p in arrPerson)
{
Console.WriteLine(p);
}
Console.ReadLine();
}
}
public class Person:IEnumerable
{
public Person(params string[] names)
{
_names = new string[names.Length];
names.CopyTo(_names, 0);
}
public string[] _names;
public IEnumerator GetEnumerator()
{
return new PersonEnumerator(this);
}
private string this[int index]
{
get { return _names[index]; }
set { _names[index] = value; }
}
}
public class PersonEnumerator : IEnumerator
{
private int _index = -1;
private Person _p;
public PersonEnumerator(Person p) { _p = p; }
public object Current
{
get { return _p._names[_index]; }
}
public bool MoveNext()
{
_index++;
return _index < _p._names.Length;
}
public void Reset()
{
_index = -1;
}
}
C#2中的迭代器變得非常便捷,使用關(guān)鍵字yield return關(guān)鍵字實(shí)現,下面是C#2中使用yield return的重寫(xiě)版本:
public class Test
{
static void Main()
{
Person arrPerson = new Person("oec2003","oec2004","oec2005");
foreach (string p in arrPerson)
{
Console.WriteLine(p);
}
Console.ReadLine();
}
}
public class Person:IEnumerable
{
public Person(params string[] names)
{
_names = new string[names.Length];
names.CopyTo(_names, 0);
}
public string[] _names;
public IEnumerator GetEnumerator()
{
foreach (string s in _names)
{
yield return s;
}
}
}
匿名方法比較適用于定義必須通過(guò)委托調用的方法,用多線(xiàn)程來(lái)舉個(gè)例子,在C#1中代碼如下:
private void btnTest_Click(object sender, EventArgs e)
{
Thread thread = new Thread(new ThreadStart(DoWork));
thread.Start();
}
private void DoWork()
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(100);
this.Invoke(new Action<string>(this.ChangeLabel),i.ToString());
}
}
private void ChangeLabel(string i)
{
label1.Text = i + "/100";
}
使用C#2中的匿名方法,上面的例子中可以省去DoWork和ChangeLabel兩個(gè)方法,代碼如下:
private void btnTest_Click(object sender, EventArgs e)
{
Thread thread = new Thread(new ThreadStart(delegate() {
for (int i = 0; i < 100; i++)
{
Thread.Sleep(100);
this.Invoke(new Action(delegate() { label1.Text = i + "/100"; }));
}
}));
thread.Start();
}
| C# | VS版本 | CLR版本 | .NET Framework |
|---|---|---|---|
| 3.0 | VS2008 | 2.0 | 3.0 3.5 |
如果說(shuō)C#2中的核心是泛型的話(huà),那么C#3中的核心就應是Linq了,C#3中的特性幾乎都是為L(cháng)inq服務(wù)的,但每一項特性都可以脫離Linq來(lái)使用。下面就來(lái)看下C#3中有哪些特性。
這個(gè)特性非常簡(jiǎn)單,就是使定義屬性變得更簡(jiǎn)單了。代碼如下:
public string Name { get; set; }
public int Age { private set; get; }
隱式類(lèi)型的局部變量是讓我們在定義變量時(shí)可以比較動(dòng)態(tài)化,使用var關(guān)鍵字作為類(lèi)型的占位符,然后由編譯器來(lái)推導變量的類(lèi)型。
擴展方法可以在現有的類(lèi)型上添加一些自定義的方法,比如可以在string類(lèi)型上添加一個(gè)擴展方法ToInt32,就可以像“20”.ToInt32()這樣調用了。
隱式類(lèi)型雖然讓編碼方便了,但有些不少限制:
簡(jiǎn)化了對象和集合的創(chuàng )建,具體參見(jiàn)《C#3.0學(xué)習(2)—對象集合初始化器(http://blog.fwhyy.com/2008/02/learning-c-3-0-2-object-collection-initializer/)》。
和隱式類(lèi)型的局部變量類(lèi)似,可以不用顯示指定類(lèi)型來(lái)進(jìn)行數組的定義,通常我們定義數組是這樣:
string[] names = { "oec2003", "oec2004", "oec2005" };
使用匿名類(lèi)型數組可以想下面這樣定義:
protected void Page_Load(object sender, EventArgs e)
{
GetName(new[] { "oec2003", "oec2004", "oec2005" });
}
public string GetName(string[] names)
{
return names[0];
}
匿名類(lèi)型是在初始化的時(shí)候根據初始化列表自動(dòng)產(chǎn)生類(lèi)型的一種機制,利用對象初始化器來(lái)創(chuàng )建匿名對象的對象,具體參見(jiàn)《C#3.0學(xué)習(3)—匿名類(lèi)型(http://blog.fwhyy.com/2008/03/learning-csharp-3-0-3-anonymous-types/)》。
實(shí)際上是一個(gè)匿名方法,Lambda表達的表現形式是:(參數列表)=>{語(yǔ)句},看一個(gè)例子,創(chuàng )建一個(gè)委托實(shí)例,獲取一個(gè)string類(lèi)型的字符串,并返回字符串的長(cháng)度。代碼如下:
Func<string, int> func = delegate(string s) { return s.Length; };
Console.WriteLine(func("oec2003"));
使用Lambda的寫(xiě)法如下:
Func<string, int> func = (string s)=> { return s.Length; };
Func<string, int> func1 = (s) => { return s.Length; };
Func<string, int> func2 = s => s.Length;
上面三種寫(xiě)法是逐步簡(jiǎn)化的過(guò)程。
是.NET3.5中提出的一種表達方式,提供一種抽象的方式將一些代碼表示成一個(gè)對象樹(shù)。要使用Lambda表達式樹(shù)需要引用命名空間System.Linq.Expressions,下面代碼構建一個(gè)1+2的表達式樹(shù),最終表達式樹(shù)編譯成委托來(lái)得到執行結果:
Expression a = Expression.Constant(1);
Expression b = Expression.Constant(2);
Expression add = Expression.Add(a, b);
Console.WriteLine(add); //(1+2) Func<int> fAdd = Expression.Lambda<Func<int>>(add).Compile();
Console.WriteLine(fAdd()); //3
Lambda和Lambda表達式樹(shù)為我們使用Linq提供了很多支持,如果我們在做的一個(gè)管理系統使用了Linq To Sql,在列表頁(yè)會(huì )有按多個(gè)條件來(lái)進(jìn)行數據的篩選的功能,這時(shí)就可以使用Lambda表達式樹(shù)來(lái)進(jìn)行封裝查詢(xún)條件,下面的類(lèi)封裝了And和Or兩種條件:
public static class DynamicLinqExpressions
{
public static Expression<Func<T, bool>> True<T>() { return f => true; }
public static Expression<Func<T, bool>> False<T>() { return f => false; }
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>
(Expression.Or(expr1.Body, invokedExpr), expr1.Parameters);
}
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>
(Expression.And(expr1.Body, invokedExpr), expr1.Parameters);
}
}
下面是獲取條件的方法:
public Expression<Func<Courses, bool>> GetCondition()
{
var exp = DynamicLinqExpressions.True<Courses>();
if (txtCourseName.Text.Trim().Length > 0)
{
exp = exp.And(g => g.CourseName.Contains(txtCourseName.Text.Trim()));
}
if (ddlGrade.SelectedValue != "-1")
{
exp=exp.And(g => g.GradeID.Equals(ddlGrade.SelectedValue));
}
return exp;
}
Linq是一個(gè)很大的話(huà)題,也是NET3.5中比較核心的內容,有很多書(shū)籍專(zhuān)門(mén)來(lái)介紹Linq,下面只是做一些簡(jiǎn)單的介紹,需要注意的是Linq并非是Linq To Sql,Linq是一個(gè)大的集合,里面包含:
下面以L(fǎng)inq To Object為例子來(lái)看看Linq是怎么使用的:
public class UserInfo
{
public string Name { get; set; }
public int Age { get; set; }
}
public class Test
{
static void Main()
{
List<UserInfo> users = new List<UserInfo>()
{
new UserInfo{Name="oec2003",Age=20},
new UserInfo{Name="oec2004",Age=21},
new UserInfo{Name="oec2005",Age=22}
};
IEnumerable<UserInfo> selectedUser = from user in users
where user.Age > 20
orderby user.Age descending select user;
foreach (UserInfo user in selectedUser)
{
Console.WriteLine("姓名:"+user.Name+",年齡:"+user.Age);
}
Console.ReadLine();
}
}
可以看出,Linq可以讓我們使用類(lèi)似Sql的關(guān)鍵字來(lái)對集合、對象、XML等進(jìn)行查詢(xún)。
| C# | VS版本 | CLR版本 | .NET Framework |
|---|---|---|---|
| 4.0 | VS2010 | 4.0 | 4.0 |
VB在很早就已經(jīng)支持了可選參數,而C#知道4了才支持,顧名思義,可選參數就是一些參數可以是可選的,在方法調用的時(shí)候可以不用輸入??聪旅娲a:
public class Test
{
static void Main()
{
Console.WriteLine(GetUserInfo()); //姓名:ooec2003,年齡:30
Console.WriteLine(GetUserInfo("oec2004", 20));//姓名:ooec2004,年齡:20
Console.ReadLine();
}
public static string GetUserInfo(string name = "oec2003", int age = 30)
{
return "姓名:" + name + ",年齡:" + age.ToString();
}
}
命名實(shí)參是在制定實(shí)參的值時(shí),可以同時(shí)指定相應參數的名稱(chēng)。編譯器可以判斷參數的名稱(chēng)是否正確,命名實(shí)參可以讓我們在調用時(shí)改變參數的順序。命名實(shí)參也經(jīng)常和可選參數一起使用,看下面的代碼:
static void Main()
{
Console.WriteLine(Cal());//9
Console.WriteLine(Cal(z: 5, y: 4));//25
Console.ReadLine();
}
public static int Cal(int x=1, int y=2, int z=3)
{
return (x + y) * z;
}
通過(guò)可選參數和命名參數的結合使用,我們可以減少代碼中方法的重載。
C#使用dynamic來(lái)實(shí)現動(dòng)態(tài)類(lèi)型,在沒(méi)用使用dynamic的地方,C#依然是靜態(tài)的。靜態(tài)類(lèi)型中當我們要使用程序集中的類(lèi),要調用類(lèi)中的方法,編譯器必須知道程序集中有這個(gè)類(lèi),類(lèi)里有這個(gè)方法,如果不能事先知道,編譯時(shí)會(huì )報錯,在C#4以前可以通過(guò)反射來(lái)解決這個(gè)問(wèn)題??匆粋€(gè)使用dynamic的小例子:
dynamic a = "oec2003";
Console.WriteLine(a.Length);//7
Console.WriteLine(a.length);//string 類(lèi)型不包含length屬性,但編譯不會(huì )報錯,運行時(shí)會(huì )報錯
Console.ReadLine();
您可能會(huì )發(fā)現使用dynamic聲明變量和C#3中提供的var有點(diǎn)類(lèi)似,其他他們是有本質(zhì)區別的,var聲明的變量在編譯時(shí)會(huì )去推斷出實(shí)際的類(lèi)型,var只是相當于一個(gè)占位符,而dynamic聲明的變量在編譯時(shí)不會(huì )進(jìn)行類(lèi)型檢查。
dynamic用的比較多的應該是替代以前的反射,而且性能有很大提高。假設有一個(gè)名為DynamicLib的程序集中有一個(gè)DynamicClassDemo類(lèi),類(lèi)中有一個(gè)Cal方法,下面看看利用反射怎么訪(fǎng)問(wèn)Cal方法:
namespace DynamicLib
{
public class DynamicClassDemo
{
public int Cal(int x = 1, int y = 2, int z = 3)
{
return (x + y) * z;
}
}
}
static void Main()
{
Assembly assembly = Assembly.Load("DynamicLib");
object obj = assembly.CreateInstance("DynamicLib.DynamicClassDemo");
Type type = obj.GetType();
MethodInfo method = type.GetMethod("Cal");
Console.WriteLine(method.Invoke(obj, new object[] { 1, 2, 3 }));//9
Console.ReadLine();
}
用dynamic的代碼如下:
Assembly assembly = Assembly.Load("DynamicLib");
dynamic obj = assembly.CreateInstance("DynamicLib.DynamicClassDemo");
Console.WriteLine(obj.Cal());
Console.ReadLine();
在前后端分離的模式下,WebAPI接口的參數也可以采用dynamic來(lái)定義,直接就可以解析前端傳入的json參數,不用每一個(gè)接口方法都定義一個(gè)參數類(lèi)型。不好的地方就是通過(guò)Swagger來(lái)生產(chǎn)API文檔時(shí),不能明確的知道輸入參數的每個(gè)屬性的含義。
C#4中還有一些COM互操作性的改進(jìn)和逆變性和協(xié)變性的改進(jìn),我幾乎沒(méi)有用到,所以在此就不講述了。
| C# | VS版本 | CLR版本 | .NET Framework |
|---|---|---|---|
| 5.0 | VS2012\2013 | 4.0 | 4.5 |
異步處理是C#5中很重要的一個(gè)特性,會(huì )涉及到兩個(gè)關(guān)鍵字:async和await,要講明白這個(gè)需要單獨寫(xiě)一篇來(lái)介紹。
可以簡(jiǎn)單理解為,當Winform窗體程序中有一個(gè)耗時(shí)操作時(shí),如果是同步操作,窗體在返回結果之前會(huì )卡死,當然在C#5之前的版本中有多種方法可以來(lái)解決這個(gè)問(wèn)題,但C#5的異步處理解決的更優(yōu)雅。
與其說(shuō)是一個(gè)特性,不如說(shuō)是對之前版本問(wèn)題的修復,看下面的代碼:
public static void CapturingVariables()
{
string[] names = { "oec2003","oec2004","oec2005"};
var actions = new List<Action>();
foreach(var name in names)
{
actions.Add(() => Console.WriteLine(name));
}
foreach(Action action in actions)
{
action();
}
}
這段代碼在之前的C#版本中,會(huì )連續輸出三個(gè)oec2005,在C#5中會(huì )按照我們的期望依次輸出oec2003、oec2004、oec2005。
如果您的代碼在之前的版本中有利用到這個(gè)錯誤的結果,那么在升級到C#5或以上版本中就要注意了。
我們的程序通常是以release形式發(fā)布,發(fā)布后很難追蹤到代碼執行的具體信息,在C#5中提供了三種特性(Attribute), 允許獲取調用者的當前編譯器的執行文件名、所在行數與方法或屬性名稱(chēng)。代碼如下:
static void Main(string[] args)
{
ShowInfo();
Console.ReadLine();
}
public static void ShowInfo(
[CallerFilePath] string file = null,
[CallerLineNumber] int number = 0,
[CallerMemberName] string name = null)
{
Console.WriteLine($"filepath:{file}");
Console.WriteLine($"rownumber:{number}");
Console.WriteLine($"methodname:{name}");
}
調用結果如下:
filepath:/Users/ican_macbookpro/Projects/CsharpFeature/CsharpFeature5/Program.cs
rownumber:12
methodname:Main
| C# | VS版本 | CLR版本 | .NET Framework |
|---|---|---|---|
| 6.0 | VS2015 | 4.0 | 4.6 |
在C#6中提供了不少的新功能,我認為最有用的就是Null條件運算符和字符串嵌入。
在C#中,一個(gè)常見(jiàn)的異常就是“未將對象引用到對象的實(shí)例”,原因是對引用對象沒(méi)有做非空判斷導致。在團隊中雖然再三強調,但依然會(huì )在這個(gè)問(wèn)題上栽跟頭。下面的代碼就會(huì )導致這個(gè)錯誤:
class Program
{
static void Main(string[] args)
{
//Null條件運算符
User user = null;
Console.WriteLine(user.GetUserName());
Console.ReadLine();
}
}
class User
{
public string GetUserName() => "oec2003";
}
要想不出錯,就需要對user對象做非空判斷
if(user!=null)
{
Console.WriteLine(user.GetUserName());
}
在C#6中可以用很簡(jiǎn)單的方式來(lái)處理這個(gè)問(wèn)題
//Null條件運算符
User user = null;
Console.WriteLine(user?.GetUserName());
注:雖然這個(gè)語(yǔ)法糖非常簡(jiǎn)單,也很好用,但在使用時(shí)也需要多想一步,當對象為空時(shí),調用其方法返回的值也是空,這樣的值對后續的操作會(huì )不會(huì )有影響,如果有,還是需要做判斷,并做相關(guān)的處理。
字符串嵌入可以簡(jiǎn)化字符串的拼接,很直觀(guān)的就可以知道需要表達的意思,在C#6及以上版本中都應該用這種方式來(lái)處理字符串拼接,代碼如下:
//字符串嵌入
string name = "oec2003";
//之前版本的處理方式1
Console.WriteLine("Hello " + name);
//之前版本的處理方式2
Console.WriteLine(string.Format("Hello {0}",name));
//C#6字符串嵌入的處理方式
Console.WriteLine($"Hello {name}");
| C# | VS版本 | .NET Framework |
|---|---|---|
| 7.0 | VS2017 15.0 | .NET Core1.0 |
| 7.1 | VS2017 15.3 | .NET Core2.0 |
| 7.2 | VS2017 15.5 | .NET Core2.0 |
| 7.3 | VS2017 15.7 | .NET Core2.1 |
此特性簡(jiǎn)化了out變量的使用,之前的版本中使用代碼如下:
int result = 0;
int.TryParse("20", out result);
Console.WriteLine(result);
優(yōu)化后的代碼,不需要事先定義一個(gè)變量
int.TryParse("20", out var result);
Console.WriteLine(result);
這也是一個(gè)減少我們編碼的語(yǔ)法糖,直接看代碼吧
public class PatternMatching
{
public void Test()
{
List<Person> list = new List<Person>();
list.Add(new Man());
list.Add(new Woman());
foreach (var item in list)
{
//在之前版本中此處需要做類(lèi)型判斷和類(lèi)型轉換
if (item is Man man)
Console.WriteLine(man.GetName());
else if (item is Woman woman)
Console.WriteLine(woman.GetName());
}
}
}
public abstract class Person
{
public abstract string GetName();
}
public class Man:Person
{
public override string GetName() => "Man";
}
public class Woman : Person
{
public override string GetName() => "Woman";
}
詳細參考官方文檔:https://docs.microsoft.com/zh-cn/dotnet/csharp/pattern-matching
可以在方法中寫(xiě)內部方法,在方法中有時(shí)需要在多個(gè)代碼邏輯執行相同的處理,之前的做法是在類(lèi)中寫(xiě)私有方法,現在可以讓這個(gè)私有方法寫(xiě)在方法的內部,提高代碼可讀性。
static void LocalMethod()
{
string name = "oec2003";
string name1 = "oec2004";
Console.WriteLine(AddPrefix(name));
Console.WriteLine(AddPrefix(name1));
string AddPrefix(string n)
{
return $"Hello {n}";
}
}
這個(gè)最大的好處是,在控制臺程序中調試異步方法變得很方便。
static async Task Main()
{
await SomeAsyncMethod();
}
可以限制在同一個(gè)程序集中的派生類(lèi)的訪(fǎng)問(wèn),是對protected internal的一種補強,protected internal是指同一程序集中的類(lèi)或派生類(lèi)進(jìn)行訪(fǎng)問(wèn)。
每個(gè)特性都需要我們去編碼實(shí)現下,了解了真正的含義和用途,我們才能在工作中靈活的運用。
本文所涉及到的實(shí)例代碼后面也會(huì )上傳到Github上。
希望本文對您有所幫助。
聯(lián)系客服