1. 程式人生 > >程式碼演示C#各版本新功能

程式碼演示C#各版本新功能

程式碼演示C#各版本新功能

C#各版本新功能其實都能在官網搜到,但很少有人整理在一起,並通過非常簡短的程式碼將每個新特性演示出來。

  • 程式碼演示C#各版本新功能
    • C# 2.0版 - 2005
      • 泛型
      • 分部型別
      • 匿名方法
      • 可以為null的值型別
      • 迭代器
      • 協變和逆變
    • C# 3.0版 - 2007
      • 自動實現的屬性
      • 匿名型別
      • 查詢表示式(LINQ)
      • Lambda表示式
      • 表示式樹
      • 擴充套件方法
      • var
      • 分部方法
      • 物件和集合初始值設定項
    • C# 4.0版 - 2010
      • dynamic
      • 命名引數/可選引數
      • 泛型中的協變和逆變
      • 型別等效、內建互操作型別
    • C# 5.0版 - 2012
      • async/await
      • 呼叫方資訊
    • C# 6.0版 - 2015
      • 靜態匯入
      • 異常篩選器
      • 自動初始化表示式
      • Expression-bodied 函式成員
      • Null傳播器
      • 字串內插
      • nameof表示式
      • 索引初始值設定項
    • C# 7.0版本 - 2017
      • out變數
      • 元組和解構函式
      • 模式匹配
      • 本地函式
      • 更多的expression-bodied成員
      • Ref 區域性變數和返回結果
      • 棄元
      • 二進位制文字和數字分隔符
      • throw表示式
    • C# 8.0 版 - 2019
      • Readonly 成員
      • 預設介面方法
      • 模式匹配增強
        • 屬性模式
        • Tuple模式
        • 位置模式
      • switch表示式
      • using宣告
      • 靜態本地函式
      • 非同步流
      • 索引和範圍
      • Null合併賦值
      • 非託管構造型別
      • 巢狀表示式中的 stackalloc
  • 附錄/總結

C# 2.0版 - 2005

泛型

Java中的泛型不支援值型別,且會執行時型別擦除,這一點.NET更優秀。

// Declare the generic class.
public class GenericList<T>
{
    public void Add(T input) { }
}
class TestGenericList
{
    private class ExampleClass { }
    static void Main()
    {
        // Declare a list of type int.
        GenericList<int> list1 = new GenericList<int>();
        list1.Add(1);

        // Declare a list of type string.
        GenericList<string> list2 = new GenericList<string>();
        list2.Add("");

        // Declare a list of type ExampleClass.
        GenericList<ExampleClass> list3 = new GenericList<ExampleClass>();
        list3.Add(new ExampleClass());
    }
}

分部型別

拆分一個類、一個結構、一個介面或一個方法的定義到兩個或更多的檔案中是可能的。 每個原始檔包含型別或方法定義的一部分,編譯應用程式時將把所有部分組合起來。

public partial class Employee
{
    public void DoWork()
    {
    }
}

public partial class Employee
{
    public void GoToLunch()
    {
    }
}

匿名方法

Func<int, int, int> sum = delegate (int a, int b) { return a + b; };
Console.WriteLine(sum(3, 4));  // output: 7

可以為null的值型別

double? pi = 3.14;
char? letter = 'a';

int m2 = 10;
int? m = m2;

bool? flag = null;

// An array of a nullable type:
int?[] arr = new int?[10];

迭代器

static void Main()
{
    foreach (int number in SomeNumbers())
    {
        Console.Write(number.ToString() + " ");
    }
    // Output: 3 5 8
    Console.ReadKey();
}

public static System.Collections.IEnumerable SomeNumbers()
{
    yield return 3;
    yield return 5;
    yield return 8;
}

協變和逆變

在 C# 中,協變和逆變能夠實現陣列型別、委託型別和泛型型別引數的隱式引用轉換。 協變保留分配相容性,逆變則與之相反。

// Assignment compatibility.   
string str = "test";  
// An object of a more derived type is assigned to an object of a less derived type.   
object obj = str;  
  
// Covariance.   
IEnumerable<string> strings = new List<string>();  
// An object that is instantiated with a more derived type argument   
// is assigned to an object instantiated with a less derived type argument.   
// Assignment compatibility is preserved.   
IEnumerable<object> objects = strings;  
  
// Contravariance.             
// Assume that the following method is in the class:   
// static void SetObject(object o) { }   
Action<object> actObject = SetObject;  
// An object that is instantiated with a less derived type argument   
// is assigned to an object instantiated with a more derived type argument.   
// Assignment compatibility is reversed.   
Action<string> actString = actObject;

C# 3.0版 - 2007

自動實現的屬性

// This class is mutable. Its data can be modified from
// outside the class.
class Customer
{
    // Auto-implemented properties for trivial get and set
    public double TotalPurchases { get; set; }
    public string Name { get; set; }
    public int CustomerID { get; set; }

    // Constructor
    public Customer(double purchases, string name, int ID)
    {
        TotalPurchases = purchases;
        Name = name;
        CustomerID = ID;
    }

    // Methods
    public string GetContactInfo() { return "ContactInfo"; }
    public string GetTransactionHistory() { return "History"; }

    // .. Additional methods, events, etc.
}

class Program
{
    static void Main()
    {
        // Intialize a new object.
        Customer cust1 = new Customer(4987.63, "Northwind", 90108);

        // Modify a property.
        cust1.TotalPurchases += 499.99;
    }
}

匿名型別

var v = new { Amount = 108, Message = "Hello" };  
  
// Rest the mouse pointer over v.Amount and v.Message in the following  
// statement to verify that their inferred types are int and n .  
Console.WriteLine(v.Amount + v.Message);

查詢表示式(LINQ)

LINQ允許你可以像寫SQL一樣寫C#程式碼,像這樣:

from p in persons
where p.Age > 18 && p.IsBeatiful
select new
{
    p.WeChatId, 
    p.PhoneNumber
}

LINQ的意義在於讓C#做出了重大調整,本章中說到的lambda表示式、擴充套件方法、表示式樹、匿名型別、自動屬性等,都是LINQ的必要組成部分。

由於用擴充套件方法的形式也能得到一致的結果,而且還能讓程式碼風格更加一致,所以我平時用LINQ語法較少:

// 與上文程式碼相同,但改成了擴充套件方法風格:
persons
    .Where(x => x.Age > 18 && x.IsBeatiful)
    .Select(x => new 
    {
        x.WeChatId, 
        x.PhoneNumber, 
    });

Lambda表示式

Func<int, int> square = x => x * x;
Console.WriteLine(square(5));
// Output:
// 25

表示式樹

這個是LINQ的基礎之一,它的作用是將程式碼像資料一樣,儲存在記憶體中;然後稍後對這些“程式碼資料”進行重新解釋/執行。

Entity Framework就是一個經典場景,它先將表示式樹儲存起來,然後執行時,將其翻譯為SQL發給資料庫執行。

注意:表示式樹並不能表示所有的程式碼,C# 3.0之後的語法,包含???.async await、可選引數等,都無法放到表示式樹中。據說官方準備更新它,但遲遲沒有進展。

擴充套件方法

擴充套件方法使你能夠向現有型別“新增”方法,而無需建立新的派生型別、重新編譯或以其他方式修改原始型別。

static void Main()
{
    Console.WriteLine ("Perth".IsCapitalized());    
    // Equivalent to:
    Console.WriteLine (StringHelper.IsCapitalized ("Perth"));   
    
    // Interfaces can be extended, too:
    Console.WriteLine ("Seattle".First());   // S
}

public static class StringHelper
{
    public static bool IsCapitalized (this string s)
    {
        if (string.IsNullOrEmpty(s)) return false;
        return char.IsUpper (s[0]);
    }
    
    public static T First<T> (this IEnumerable<T> sequence)
    {
        foreach (T element in sequence)
            return element;
        
        throw new InvalidOperationException ("No elements!");
    }
}

var

var i = 10; // Implicitly typed.
int i = 10; // Explicitly typed.

分部方法

namespace PM
{
    partial class A
    {
        partial void OnSomethingHappened(string s);
    }

    // This part can be in a separate file.
    partial class A
    {
        // Comment out this method and the program
        // will still compile.
        partial void OnSomethingHappened(String s)
        {
            Console.WriteLine("Something happened: {0}", s);
        }
    }
}

物件和集合初始值設定項

public class Cat
{
    // Auto-implemented properties.
    public int Age { get; set; }
    public string Name { get; set; }

    public Cat()
    {
    }

    public Cat(string name)
    {
        this.Name = name;
    }
}

C# 4.0版 - 2010

dynamic

這個是特性使得CLR不得不進行一次修改。有了這個,C#也能像jsphppython等弱型別語言一樣寫程式碼了。

dynamic a = 3;
a = 3.14;
a = "Hello World";
a = new[] { 1, 2, 3, 4, 5 };
a = new Func<int>(() => 3);
a = new StringBuilder();
Console.WriteLine(a.GetType().Name); // StringBuilder

注意dynamic可以表示任何東西,包含陣列、委託等等。濫用dynamic容易讓程式變得很難維護。

命名引數/可選引數

PrintOrderDetails(productName: "Red Mug", sellerName: "Gift Shop", orderNum: 31);
public void ExampleMethod(int required, string optionalstr = "default string",
    int optionalint = 10)

泛型中的協變和逆變

IEnumerable<Derived> d = new List<Derived>();
IEnumerable<Base> b = d;
Action<Base> b = (target) => { Console.WriteLine(target.GetType().Name); };
Action<Derived> d = b;
d(new Derived());

型別等效、內建互操作型別

這個主要是為了和COM進行互動。之前需要引用一些COM型別相關的程式集,現在可以直接引用COM
具體可以參見:https://docs.microsoft.com/zh-cn/dotnet/framework/interop/type-equivalence-and-embedded-interop-types

C# 5.0版 - 2012

async/await

private DamageResult CalculateDamageDone()
{
    // Code omitted:
    //
    // Does an expensive calculation and returns
    // the result of that calculation.
}

calculateButton.Clicked += async (o, e) =>
{
    // This line will yield control to the UI while CalculateDamageDone()
    // performs its work.  The UI thread is free to perform other work.
    var damageResult = await Task.Run(() => CalculateDamageDone());
    DisplayDamage(damageResult);
};

async/await的本質是狀態機,像IEnumerable<T>一樣。以前遊戲引擎Unity只支援C# 3.0,因此當時它用狀態機發Http請求是用的IEnumerable<T>

async/await有兩個好處,一是可以避免UI執行緒卡頓,二是提高系統吞吐率,最終提高效能。

呼叫方資訊

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

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

// Sample Output:
//  message: Something happened.
//  member name: DoProcessing
//  source file path: c:\Visual Studio Projects\CallerInfoCS\CallerInfoCS\Form1.cs
//  source line number: 31

注意這個是編譯期生成的,因此比StackTrace更能保證效能。

C# 6.0版 - 2015

靜態匯入

終於可以不用寫靜態類名了。

using static System.Math;
using static System.Console;

WriteLine(Sin(3.14)); // 0.00159265291648683

異常篩選器

try-catch時,可以按指定的條件進行catch,其它條件不catch

public static async Task<string> MakeRequest()
{
    WebRequestHandler webRequestHandler = new WebRequestHandler();
    webRequestHandler.AllowAutoRedirect = false;
    using (HttpClient client = new HttpClient(webRequestHandler))
    {
        var stringTask = client.GetStringAsync("https://docs.microsoft.com/en-us/dotnet/about/");
        try
        {
            var responseText = await stringTask;
            return responseText;
        }
        catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("301"))
        {
            return "Site Moved";
        }
    }
}

自動初始化表示式

public ICollection<double> Grades { get; } = new List<double>();

Expression-bodied 函式成員

public override string ToString() => $"{LastName}, {FirstName}";

Null傳播器

var first = person?.FirstName;

字串內插

public string GetGradePointPercentage() =>
    $"Name: {LastName}, {FirstName}. G.P.A: {Grades.Average():F2}";

nameof表示式

有時字串值和某個變數名稱一致,尤其是在做引數驗證時。這裡nameof就能在編譯期,自動從變數名生成一個字串。

if (IsNullOrWhiteSpace(lastName))
    throw new ArgumentException(message: "Cannot be blank", paramName: nameof(lastName));

索引初始值設定項

使集合初始化更容易的另一個功能是對 Add 方法使用擴充套件方法 。 新增此功能的目的是進行 Visual Basic 的奇偶校驗。 如果自定義集合類的方法具有通過語義方式新增新項的名稱,則此功能非常有用。

C# 7.0版本 - 2017

out變數

if (int.TryParse(input, out int result))
    Console.WriteLine(result);
else
    Console.WriteLine("Could not parse input");

元組和解構函式

(string Alpha, string Beta) namedLetters = ("a", "b");
Console.WriteLine($"{namedLetters.Alpha}, {namedLetters.Beta}");

解構函式應該類似C++中的解構函式,在例項回收時執行?

模式匹配

現在可以在匹配一個型別時,自動轉換為這個型別的變數,如果轉換失敗,這個變數就賦值為預設值(null0)。

極簡版:

if (input is int count)
    sum += count;

switch/case版:

public static int SumPositiveNumbers(IEnumerable<object> sequence)
{
    int sum = 0;
    foreach (var i in sequence)
    {
        switch (i)
        {
            case 0:
                break;
            case IEnumerable<int> childSequence:
            {
                foreach(var item in childSequence)
                    sum += (item > 0) ? item : 0;
                break;
            }
            case int n when n > 0:
                sum += n;
                break;
            case null:
                throw new NullReferenceException("Null found in sequence");
            default:
                throw new InvalidOperationException("Unrecognized type");
        }
    }
    return sum;
}

本地函式

這個主要是方便,javascript就能這樣寫。

lambda的好處在於,這個可以定義在後面,而lambda必須定義在前面。

public static IEnumerable<char> AlphabetSubset3(char start, char end)
{
    if (start < 'a' || start > 'z')
        throw new ArgumentOutOfRangeException(paramName: nameof(start), message: "start must be a letter");
    if (end < 'a' || end > 'z')
        throw new ArgumentOutOfRangeException(paramName: nameof(end), message: "end must be a letter");

    if (end <= start)
        throw new ArgumentException($"{nameof(end)} must be greater than {nameof(start)}");

    return alphabetSubsetImplementation();

    IEnumerable<char> alphabetSubsetImplementation()
    {
        for (var c = start; c < end; c++)
            yield return c;
    }
}

更多的expression-bodied成員

該功能可以讓一些函式寫成表示式的形式,非常的方便。

// Expression-bodied constructor
public ExpressionMembersExample(string label) => this.Label = label;

// Expression-bodied finalizer
~ExpressionMembersExample() => Console.Error.WriteLine("Finalized!");

private string label;

// Expression-bodied get / set accessors.
public string Label
{
    get => label;
    set => this.label = value ?? "Default label";
}

Ref 區域性變數和返回結果

此功能允許使用並返回對變數的引用的演算法,這些變數在其他位置定義。 一個示例是使用大型矩陣並查詢具有某些特徵的單個位置。

這個功能主要是為了提高值型別的效能,讓它真正發揮其作用。C++就有類似的功能。

public static ref int Find(int[,] matrix, Func<int, bool> predicate)
{
    for (int i = 0; i < matrix.GetLength(0); i++)
        for (int j = 0; j < matrix.GetLength(1); j++)
            if (predicate(matrix[i, j]))
                return ref matrix[i, j];
    throw new InvalidOperationException("Not found");
}
ref var item = ref MatrixSearch.Find(matrix, (val) => val == 42);
Console.WriteLine(item);
item = 24;
Console.WriteLine(matrix[4, 2]);

棄元

通常,在進行元組解構或使用out引數呼叫方法時,必須定義一個其值無關緊要且你不打算使用的變數。 為處理此情況,C#增添了對棄元的支援 。 棄元是一個名為_的只寫變數,可向單個變數賦予要放棄的所有值。 棄元類似於未賦值的變數;不可在程式碼中使用棄元(賦值語句除外)。

using System;
using System.Collections.Generic;

public class Example
{
    public static void Main()
    {
        var (_, _, _, pop1, _, pop2) = QueryCityDataForYears("New York City", 1960, 2010);

        Console.WriteLine($"Population change, 1960 to 2010: {pop2 - pop1:N0}");
    }
   
    private static (string, double, int, int, int, int) QueryCityDataForYears(string name, int year1, int year2)
    {
        int population1 = 0, population2 = 0;
        double area = 0;
      
        if (name == "New York City")
        {
            area = 468.48; 
            if (year1 == 1960)
            {
                population1 = 7781984;
            }
            if (year2 == 2010)
            {
                population2 = 8175133;
            }
            return (name, area, year1, population1, year2, population2);
        }

        return ("", 0, 0, 0, 0, 0);
    }
}
// The example displays the following output:
//      Population change, 1960 to 2010: 393,149

二進位制文字和數字分隔符

這個用於使數字和二進位制更可讀。

// 二進位制文字:
public const int Sixteen =   0b0001_0000;
public const int ThirtyTwo = 0b0010_0000;
public const int SixtyFour = 0b0100_0000;
public const int OneHundredTwentyEight = 0b1000_0000;

// 數字分隔符:
public const long BillionsAndBillions = 100_000_000_000;
public const double AvogadroConstant = 6.022_140_857_747_474e23;
public const decimal GoldenRatio = 1.618_033_988_749_894_848_204_586_834_365_638_117_720_309_179M;

throw表示式

throw之前必須是一個語句,因此有時不得不寫更多的程式碼來完成所需功能。但7.0提供了throw表示式來使程式碼更簡潔,閱讀更輕鬆。

void Main()
{
    // You can now throw expressions in expressions clauses.
    // This is useful in conditional expressions:
    
    string result = new Random().Next(2) == 0 ? "Good" : throw new Exception ("Bad");
    result.Dump();
    
    Foo().Dump();
}

public string Foo() => throw new NotImplementedException();

C# 8.0 版 - 2019

Readonly 成員

public readonly override string ToString() =>
    $"({X}, {Y}) is {Distance} from the origin";

預設介面方法

介面中也能定義方法了,這個新功能經常受到爭論。但想想,有時是先定義介面,而實現介面需要實現很多相關、但又繁瑣的功能,如ASP.NET Core中的ILogger,誰用誰知道,特別多需要實現的方法,但又都差不多。因此所以這個功能其實很有必要。

void Main()
{
    ILogger foo = new Logger();
    foo.Log (new Exception ("test"));   
}

class Logger : ILogger
{   
    public void Log (string message) => Console.WriteLine (message);
}

interface ILogger
{
    void Log (string message);  
    
    // Adding a new member to an interface need not break implementors:
    public void Log (Exception ex) => Log (ExceptionHeader + ex.Message);
    
    // The static modifier (and other modifiers) are now allowed:
    static string ExceptionHeader = "Exception: ";
}

模式匹配增強

這個是為簡化程式碼、函數語言程式設計而生的,我個人非常喜歡。

屬性模式

public static decimal ComputeSalesTax(Address location, decimal salePrice) =>
    location switch
    {
        { State: "WA" } => salePrice * 0.06M,
        { State: "MN" } => salePrice * 0.75M,
        { State: "MI" } => salePrice * 0.05M,
        // other cases removed for brevity...
        _ => 0M
    };

Tuple模式

public static string RockPaperScissors(string first, string second)
    => (first, second) switch
    {
        ("rock", "paper") => "rock is covered by paper. Paper wins.",
        ("rock", "scissors") => "rock breaks scissors. Rock wins.",
        ("paper", "rock") => "paper covers rock. Paper wins.",
        ("paper", "scissors") => "paper is cut by scissors. Scissors wins.",
        ("scissors", "rock") => "scissors is broken by rock. Rock wins.",
        ("scissors", "paper") => "scissors cuts paper. Scissors wins.",
        (_, _) => "tie"
    };

位置模式

static Quadrant GetQuadrant(Point point) => point switch
{
    (0, 0) => Quadrant.Origin,
    var (x, y) when x > 0 && y > 0 => Quadrant.One,
    var (x, y) when x < 0 && y > 0 => Quadrant.Two,
    var (x, y) when x < 0 && y < 0 => Quadrant.Three,
    var (x, y) when x > 0 && y < 0 => Quadrant.Four,
    var (_, _) => Quadrant.OnBorder,
    _ => Quadrant.Unknown
};

switch表示式

這個功能能使程式碼從大量的if/elseswitch/case變成“一行程式碼”,符合函數語言程式設計的思想,非常好用!

public static RGBColor FromRainbow(Rainbow colorBand) =>
    colorBand switch
    {
        Rainbow.Red    => new RGBColor(0xFF, 0x00, 0x00),
        Rainbow.Orange => new RGBColor(0xFF, 0x7F, 0x00),
        Rainbow.Yellow => new RGBColor(0xFF, 0xFF, 0x00),
        Rainbow.Green  => new RGBColor(0x00, 0xFF, 0x00),
        Rainbow.Blue   => new RGBColor(0x00, 0x00, 0xFF),
        Rainbow.Indigo => new RGBColor(0x4B, 0x00, 0x82),
        Rainbow.Violet => new RGBColor(0x94, 0x00, 0xD3),
        _              => throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand)),
    };

using宣告

static int WriteLinesToFile(IEnumerable<string> lines)
{
    using var file = new System.IO.StreamWriter("WriteLines2.txt");
    // Notice how we declare skippedLines after the using statement.
    int skippedLines = 0;
    foreach (string line in lines)
    {
        if (!line.Contains("Second"))
        {
            file.WriteLine(line);
        }
        else
        {
            skippedLines++;
        }
    }
    // Notice how skippedLines is in scope here.
    return skippedLines;
    // file is disposed here
}

靜態本地函式

相比非靜態本地函式,靜態本地函式沒有閉包,因此生成的程式碼更少,效能也更容易控制。

int M()
{
    int y = 5;
    int x = 7;
    return Add(x, y);

    static int Add(int left, int right) => left + right;
}

非同步流

這個功能和IEnumerable<T>Task<T>對應,一個經典的表格如下:
| | 單值 | 多值 |
| ---- | ------- | -------------- |
| 同步 | T | IEnumerable |
| 非同步 | Task | ?

其中,這個問號?終於有了答案,它就叫非同步流——IAsyncEnumerable<T>

public static async System.Collections.Generic.IAsyncEnumerable<int> GenerateSequence()
{
    for (int i = 0; i < 20; i++)
    {
        await Task.Delay(100);
        yield return i;
    }
}

不像IEnumerable<T>IAsyncEnumerable<T>系統還沒有內建擴充套件方法,因此可能沒有IEnumerable<T>方便,但是可以通過安裝NuGetf來實現和IEnumerable<T>一樣(或者更爽)的效果。

索引和範圍

Python中的切片器一樣,只是-^代替了。

var words = new string[]
{
                // index from start    index from end
    "The",      // 0                   ^9
    "quick",    // 1                   ^8
    "brown",    // 2                   ^7
    "fox",      // 3                   ^6
    "jumped",   // 4                   ^5
    "over",     // 5                   ^4
    "the",      // 6                   ^3
    "lazy",     // 7                   ^2
    "dog"       // 8                   ^1
};              // 9 (or words.Length) ^0

var quickBrownFox = words[1..4];
var lazyDog = words[^2..^0];
var allWords = words[..]; // contains "The" through "dog".
var firstPhrase = words[..4]; // contains "The" through "fox"
var lastPhrase = words[6..]; // contains "the", "lazy" and "dog"

Null合併賦值

List<int> numbers = null;
int? i = null;

numbers ??= new List<int>();
numbers.Add(i ??= 17);
numbers.Add(i ??= 20);

Console.WriteLine(string.Join(" ", numbers));  // output: 17 17
Console.WriteLine(i);  // output: 17

非託管構造型別

與任何非託管型別一樣,可以建立指向此型別的變數的指標,或針對此型別的例項在堆疊上分配記憶體塊

Span<Coords<int>> coordinates = stackalloc[]
{
    new Coords<int> { X = 0, Y = 0 },
    new Coords<int> { X = 0, Y = 3 },
    new Coords<int> { X = 4, Y = 0 }
};

巢狀表示式中的 stackalloc

Span<int> numbers = stackalloc[] { 1, 2, 3, 4, 5, 6 };
var ind = numbers.IndexOfAny(stackalloc[] { 2, 4, 6 ,8 });
Console.WriteLine(ind);  // output: 1

附錄/總結

這麼多功能,你印象最深刻的是哪個呢?

參考資料:C#發展歷史 - C#指南 | Microsoft Docs https://docs.microsoft.com/zh-cn/dotnet/csharp/whats-new/csharp-version-history

本文內容和程式碼由肖鵬整理,有修改;轉載已獲得肖鵬本人授權。肖鵬是我公司從Java.NET的同事。原文連結為:https://akiyax.github.io/new-features-in-csharp/。

喜歡的朋友請關注我的微信公眾號:【DotNet騷操作】

相關推薦

程式碼演示C#版本功能

程式碼演示C#各版本新功能 C#各版本新功能其實都能在官網搜到,但很少有人整理在一起,並通過非常簡短的程式碼將每個新特性演示出來。 程式碼演示C#各版本新功能 C# 2.0版 - 2005 泛型 分部型別 匿名方法 可以為null的值型別 迭代器 協變和逆變 C# 3.0版 - 2007 自動實現的

C#版本增加功能

  本系列文章主要整理並介紹 C# 各版本的新增功能。 C# 8.0 C#8.0 於 2019年4月 隨 .NET Framework 4.8 與 Visual Studio 2019 一同釋出,但是當前處於預覽狀態。預計在2019年9月正式釋出。 目前提供以下功能可供試用: Readonly 成

PHP 5.2、5.3、5.4、5.5、5.6 版本功能詳解

總結 PHP5.2 以前:autoload, PDO 和 MySQLi, 型別約束 PHP5.2:JSON 支援 PHP5.3:棄用的功能,匿名函式,新增魔術方法,名稱空間,後期靜態繫結,Heredo

Kafka 0.11版本功能介紹 —— 空消費組延時rebalance

次數 新功能 ins 效果 可控 size style soft font   在0.11之前的版本中,多個consumer實例加入到一個空消費組將導致多次的rebalance,這是由於每個consumer instance啟動的時間不可控,很有可能超出coordinato

Apache Kafka 0.11版本功能簡介

多個 spa 實現 cer true assign 線程 cto headers Apache Kafka近日推出0.11版本。這是一個裏程碑式的大版本,特別是Kafka從這個版本開始支持“exactly-once”語義(下稱EOS, exactly-once semant

【翻譯】《向“彈跳球”演示程序添加功能》 in MDN

dev tps sts color htm html nes obj doc 文章地址: https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Objects/%E5%90%91%E2%80%9C%E5%BC%B

Kotlin的特點及版本特性

文章地址:sguotao.top/Kotlin-2018… Kotlin語言的特點 Kotlin語義是一座小島,是一種在Java虛擬機器上執行的靜態型別程式語言,Kotlin的目的就是要兼具現代程式語言的所有優點,同時還要具有Java語言的跨平臺性,並且要做到簡潔。它也可以被編譯成為JavaScript原

【java】java版本特性總結

Java5: 1、泛型 Generics:         引用泛型之後,允許指定集合裡元素的型別,免去了強制型別轉換,並且能在編譯時刻進行型別檢查的好處。     &nb

nginx rtmp module 程式碼詳解 模組主要功能

ngx_rtmp_dash_module http模組裡播放MPEG-DASH相關處理 ngx_rtmp_mp4_module 主要支援rtmp MP4這塊點播相關功能,支援seek操作 ngx_rtmp_flv_module 主要是flv檔案格式的點播相關功能,支援seek操作 ng

MCSA / Windows Server 2016版本功能及比較,安裝需求及選項

watermark http mage ext ddd water 技術分享 fde mark MCSA / Windows Server 2016各版本的功能及比較,安裝需求及選項

hasura graphql-engine v1.0.0-alpha30 版本功能介紹

hasura graphql-engine v1.0.0-alpha30 釋出了,以下為一些變動的簡單說明 破壞性的變動 order_by 中的desc 從 desc nulls last 修改為 desc nulls first ,具有更好的效能,但是從測試上,29 版本與30 版本的都是支援的

故人的一份連連看程式碼c語言版本

花一天時間寫的一個連連看,唉!分支限界有的關鍵點,還是不是掌握的很清楚,居然搞那麼長時間,應該 在3個小時之內輕鬆拿下的,加油了 // MyLinkup.cpp : Defines the entry point for the console application. // #in

Android 系統版本特性總結

Android 4.0 Space 留白 PopupMenu GlidLayout 網格佈局 Android 5.0 MaterialDesign設計風格 Material Theme Meterial Dialog CardView RecyclerView

Spring 版本特性

Spring各版本新特性 1.Spring3.1新特性 (2)SpringMVC功能增強: (3)不再支援的功能: 為支援原生的EntityManager,Spring的Jpa

JDK1.5以後版本特性

JDK各個版本的新特性   對於很多剛接觸java語言的初學者來說,要了解一門語言,最好的方式就是要能從基礎的版本進行了解,升級的過程,以及升級的新特性,這樣才能循序漸進的學好一門語言。今天先為大家介紹一下JDK1.5版本到JDK1.7版本的特性。希望能給予幫助。 JDK1.5新特性: 1.自

hasura graphql-engine v1.0.0-alpha30 版本功能介紹

hasura graphql-engine v1.0.0-alpha30 釋出了,以下為一些變動的簡單說明 破壞性的變動 order_by 中的desc 從 desc nulls last 修改為 desc nulls first ,具有更好的效能,但是從測試上,29 版本與30 版本的都是支援的

C++11的功能特性對Boost庫影響

《Boost程式庫探祕——深度解析C++準標準庫》之試讀         前一陣子還看到一篇文章,說C#要重蹈C++的覆轍,這裡說的C++的覆轍是什麼呢?是指C++語言過於臃腫的功能特性,導致學習人員的流失。文章說,語言最後的威力是“開發軟體”,而不是“比拼新特性”    

Android P版本 功能介紹和相容性處理(一)

Android P版本已經到來,首篇我們當然要先看下Android P版本的搭建和模擬器的使用 1: Android studio的版本請選用 Android Studio 3.1 或者 Android Studio 3.2 Canary; Androi

C#每個版本特性

一直都認為學習語言應該系統的進行學習,瞭解每一個版本的新增特性,才能在實際應用中做到有的放矢。最近發現團隊中有不少人雖然用著最新的

自從昨天我發現VS2012可以編譯出支援XP的程式碼之後,我決定在GacUI裡面全面使用C++11功能

昨天研究發現,只要安裝了Update 1的Visual Studio 2012也可以編譯出支援XP的程式了。為了讓GacUI可以順利執行在XP上(雖然現在因為兩個api還不能),我一直試圖讓GacUI相容VS2010。VS2010對lambda的支援的bug很多,所以導致GacUI無法全面使用C+