1. 程式人生 > >C# 8.0的計劃特性

C# 8.0的計劃特性

await pan 常用 break com [] 泛型 enum inf

雖然現在C# 7才發布不久,並且新的版本和特性還在增加中,但是C# 8.0已經為大家公開了一些未來可能出現的新特性。

技術分享圖片

*註:以下特性只是計劃,可能在將來的正式版本會有一些差異

1.Nullable Reference Types

該特性其實本來計劃在C#7.x中就引入,但是卻被推遲到了下一個版本中。目的是為了避免引用為null的時候而導致的錯誤。

其核心思想是允許變量類型定義指定是否可以為它們分配空值:

1 IWeapon? canBeNull;
2 IWeapon cantBeNull;
canBeNull = null;       // no warning
cantBeNull = null;      // warning
cantBeNull = canBeNull; // warning

此時當申明可為nullable的對象賦值為null的時候,編譯器就不會提示警告。

canBeNull.Repair();       // warning
cantBeNull.Repair();      // no warning
if (canBeNull != null) {
    cantBeNull.Repair();  // no warning
}

2.Records

records是一個新的語法糖,它簡化了原來創建簡單類的過程,通過一條語句就可以創建出一個標準的C# 類。

例如下面的代碼:

public class Sword(int Damage, int Durability);

它相對於原來的寫法是:

public class Sword : IEquatable<Sword>
{
    public int Damage { get; }
    public int Durability { get; }

    public Sword(int Damage, int Durability)
    {
        this.Damage = Damage;
        this.Durability = Durability;
    }

    public bool Equals(Sword other)
    {
        return Equals(Damage, other.Damage) && Equals(Durability, other.Durability);
    }

    public override bool Equals(object other)
    {
        return (other as Sword)?.Equals(this) == true;
    }

    public override int GetHashCode()
    {
        return (Damage.GetHashCode() * 17 + Durability.GetHashCode());
    }

    public void Deconstruct(out int Damage, out int Durability)
    {
        Damage = this.Damage;
        Durability = this.Durability;
    }

    public Sword With(int Damage = this.Damage, int Durability = this.Durability) => 
        new Sword(Damage, Durability);
}

上面的代碼段可以看出,該類具有只讀屬性和初始化它們的構造函數。它實現值的比較,並且重寫了GetHashCode,以便在基於哈希的集合中使用,如Dictionary 和 Hashtable。

同時我們還看到在倒數第二個方法是一個解構的方法,它允許我們將Record所創建的對象進行解構為一個元組(關於解構的特性,可以參加C#7.0的特性)

var (damage, durability) = sword;

最後的一個With方法可以供我們創建一個不同屬性值的Sword副本對象。

var (damage, durability) = sword;

當然,對於With的方法,C# 也提供了一個語法糖寫法:

var strongerSword = sword with { Damage = 8 };

3.Default Interface Methods

在以往的C# 語法中,我們都知道一個Interface只能夠申明方法體,卻不能對其進行實現:

interface ISample
{
    void M1();                                    // allowed
    void M2() => Console.WriteLine("ISample.M2"); // not allowed
}

按照以往的寫法,我們一般是嘗試寫一些抽象類來作為替代實現:

abstract class SampleBase
{
    public abstract void M1();
    public void M2() => Console.WriteLine("SampleBase.M2");
}

但在C# 8.0中可能引入接口的方法實現功能。

4.Asynchronous Streams

C# 目前是已經支持了叠代器( iterators ) 和 異步方法。在C#8.0中打算結合現有的兩者,推出異步的叠代器,它將基於異步的 IEnumerable 和 IEnumerator 接口:

public interface IAsyncEnumerable<out T>
{
    IAsyncEnumerator<T> GetAsyncEnumerator();
}

public interface IAsyncEnumerator<out T> : IAsyncDisposable
{
    Task<bool> MoveNextAsync();
    T Current { get; }
}

此外,使用異步叠代器還需要IDisposable接口的異步版本:

public interface IAsyncDisposable
{
    Task DisposeAsync();
}

接下來,在使用的時候,可能看上去就像下面這樣:

var enumerator = enumerable.GetAsyncEnumerator();
try
{
    while (await enumerator.WaitForNextAsync())
    {
        while (true)
        {
            Use(enumerator.Current);
        }
    }
}
finally
{
    await enumerator.DisposeAsync();
}

當然,這個寫法對我們C#的開發人員來說可能還不是太眼熟,因為在傳統的叠代器寫法上,我們已經習慣了Foreach的寫法,因此對於異步叠代器來說,它也會存在對應的一個foreach版本,就如同下面這樣:

foreach await (var item in enumerable)
{
    Use(item);
}
async IAsyncEnumerable<int> AsyncIterator()
{
    try
    {
        for (int i = 0; i < 100; i++)
        {
            yield await GetValueAsync(i);
        }
    }
    finally
    {
        await HandleErrorAsync();
    }
}

5.Ranges

這個特性可能相對來說就比較有趣了,它允許我們使用簡短的語法來定義一個區間值,比如:

var range = 1..5;

這樣就產生了一個表示已聲明範圍的結構:

struct Range : IEnumerable<int>
{
    public Range(int start, int end);
    public int Start { get; }
    public int End { get; }
    public StructRangeEnumerator GetEnumerator();
    // overloads for Equals, GetHashCode...
}

在實際的應用過程中,我們可以這樣來使用它:

Span<T> this[Range range]
{
    get
    {
        return ((Span<T>)this).Slice(start: range.Start, length: range.End - range.Start);
    }
}
foreach (var index in min..max)
{
    // process values
}
switch (value)
{
    case 1..5:
        // value in range
        break;
}

這個特性看上去果然非常的good。

6.Generic Attributes

對泛型特性的支持將為需要類型作為參數的屬性提供更好的語法。目前,只能使用以下語法將類型傳遞給特性:

public class TypedAttribute : Attribute
{
    public TypedAttribute(Type type)
    {
        // ...
    }
}

當有了泛型特性之後,我們可以嘗試這樣做:

public class TypedAttribute<T> : Attribute
{
    public TypedAttribute()
    {
        // ...
    }
}

  

public TypedAttribute(T value)
{
    // ...
}

7.Default Literal in Deconstruction

在C# 7.x中引入了default 默認值和解構的概念。在C# 8中將實現兩者的共同作用。

要為C#7中的元組的所有成員分配默認值,必須使用元組賦值語法:

(int x, int y) = (default, default);

通過支持解構語法中的默認文字,以下語法也可以實現相同的功能:

(int x, int y) = default;

8.Caller Argument Expression

在C#5中,引入了CallerMemberName, CallerFilePath and CallerLineNumber特性,方便我們能夠獲取到有關調用方法的一些信息。

就像CallerMemberName在INotifyPropertyChanged中的應用,對於WPF開發的童鞋就在熟悉不過了:

class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    private int property;

    public int Property
    {
        get { return property; }
        set
        {
            if (value != property)
            {
                property = value;
                OnPropertyChanged();
            }
        }
    }
}

在C#8中可能會引入一個叫做CallerArgumentExpression的特性,它捕獲調用方法中的參數:

public Validate(int[] array, [CallerArgumentExpression("array")] string arrayExpression = null)
{
    if (array == null)
    {
        throw new ArgumentNullException(nameof(array), $"{arrayExpression} was null.");
    }
    if (array.Length == 0)
    {
        throw new ArgumentException($"{arrayExpression} was empty.", nameof(array));
    }
}

9.Target-typed new Expression

這可能也將成為將來常用的一個新特性,它將更加簡化在申明時候的類型推斷。

比如以往我們申明一個對象是這個樣子的:

Dictionary<string, string> dictionary = new Dictionary<string, string>(); // without var keyword
var dictionary = new Dictionary<string, string>(); // with var keyword

但是在C#8中,將簡化成這樣:

class DictionaryWrapper
{
    private Dictionary<string, string> dictionary = new();
    // ...
}

Over:

當然距離C#8真是發布可能還要等一段時間,期間可能也會增加一些其他的特性,真正的體驗效果還是一起期待8.0的發布吧

C# 8.0的計劃特性