前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C# 8.0的计划特性

C# 8.0的计划特性

作者头像
句幽
发布2020-04-27 12:02:08
6330
发布2020-04-27 12:02:08
举报
文章被收录于专栏:一起玩转.NET一起玩转.NET

虽然现在C# 7才发布不久,并且新的版本和特性还在增加中,但是C# 8.0已经为大家公开了一些未来可能出现的新特性。

*注:以下特性只是计划,可能在将来的正式版本会有一些差异

1.Nullable Reference Types

该特性其实本来计划在C#7.x中就引入,但是却被推迟到了下一个版本中。目的是为了避免引用为null的时候而导致的错误。

其核心思想是允许变量类型定义指定是否可以为它们分配空值:

代码语言:javascript
复制
1 IWeapon? canBeNull;
2 IWeapon cantBeNull;
代码语言:javascript
复制
canBeNull = null;       // no warning
cantBeNull = null;      // warning
cantBeNull = canBeNull; // warning

此时当申明可为nullable的对象赋值为null的时候,编译器就不会提示警告。

代码语言:javascript
复制
canBeNull.Repair();       // warning
cantBeNull.Repair();      // no warning
if (canBeNull != null) {
    cantBeNull.Repair();  // no warning
}

2.Records

records是一个新的语法糖,它简化了原来创建简单类的过程,通过一条语句就可以创建出一个标准的C# 类。

例如下面的代码:

代码语言:javascript
复制
public class Sword(int Damage, int Durability);

它相对于原来的写法是:

代码语言:javascript
复制
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的特性)

代码语言:javascript
复制
var (damage, durability) = sword;

最后的一个With方法可以供我们创建一个不同属性值的Sword副本对象。

代码语言:javascript
复制
var (damage, durability) = sword;

当然,对于With的方法,C# 也提供了一个语法糖写法:

代码语言:javascript
复制
var strongerSword = sword with { Damage = 8 };

3.Default Interface Methods

在以往的C# 语法中,我们都知道一个Interface只能够申明方法体,却不能对其进行实现:

代码语言:javascript
复制
interface ISample
{
    void M1();                                    // allowed
    void M2() => Console.WriteLine("ISample.M2"); // not allowed
}

按照以往的写法,我们一般是尝试写一些抽象类来作为替代实现:

代码语言:javascript
复制
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 接口:

代码语言:javascript
复制
public interface IAsyncEnumerable<out T>
{
    IAsyncEnumerator<T> GetAsyncEnumerator();
}

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

此外,使用异步迭代器还需要IDisposable接口的异步版本:

代码语言:javascript
复制
public interface IAsyncDisposable
{
    Task DisposeAsync();
}

接下来,在使用的时候,可能看上去就像下面这样:

代码语言:javascript
复制
var enumerator = enumerable.GetAsyncEnumerator();
try
{
    while (await enumerator.WaitForNextAsync())
    {
        while (true)
        {
            Use(enumerator.Current);
        }
    }
}
finally
{
    await enumerator.DisposeAsync();
}

当然,这个写法对我们C#的开发人员来说可能还不是太眼熟,因为在传统的迭代器写法上,我们已经习惯了Foreach的写法,因此对于异步迭代器来说,它也会存在对应的一个foreach版本,就如同下面这样:

代码语言:javascript
复制
foreach await (var item in enumerable)
{
    Use(item);
}
代码语言:javascript
复制
async IAsyncEnumerable<int> AsyncIterator()
{
    try
    {
        for (int i = 0; i < 100; i++)
        {
            yield await GetValueAsync(i);
        }
    }
    finally
    {
        await HandleErrorAsync();
    }
}

5.Ranges

这个特性可能相对来说就比较有趣了,它允许我们使用简短的语法来定义一个区间值,比如:

代码语言:javascript
复制
var range = 1..5;

这样就产生了一个表示已声明范围的结构:

代码语言:javascript
复制
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...
}

在实际的应用过程中,我们可以这样来使用它:

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

这个特性看上去果然非常的good。

6.Generic Attributes

对泛型特性的支持将为需要类型作为参数的属性提供更好的语法。目前,只能使用以下语法将类型传递给特性:

代码语言:javascript
复制
public class TypedAttribute : Attribute
{
    public TypedAttribute(Type type)
    {
        // ...
    }
}

当有了泛型特性之后,我们可以尝试这样做:

代码语言:javascript
复制
public class TypedAttribute<T> : Attribute
{
    public TypedAttribute()
    {
        // ...
    }
}
代码语言:javascript
复制
public TypedAttribute(T value)
{
    // ...
}

7.Default Literal in Deconstruction

在C# 7.x中引入了default 默认值和解构的概念。在C# 8中将实现两者的共同作用。

要为C#7中的元组的所有成员分配默认值,必须使用元组赋值语法:

代码语言:javascript
复制
(int x, int y) = (default, default);

通过支持解构语法中的默认文字,以下语法也可以实现相同的功能:

代码语言:javascript
复制
(int x, int y) = default;

8.Caller Argument Expression

在C#5中,引入了CallerMemberName, CallerFilePath and CallerLineNumber特性,方便我们能够获取到有关调用方法的一些信息。

就像CallerMemberName在INotifyPropertyChanged中的应用,对于WPF开发的童鞋就在熟悉不过了:

代码语言:javascript
复制
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的特性,它捕获调用方法中的参数:

代码语言:javascript
复制
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

这可能也将成为将来常用的一个新特性,它将更加简化在申明时候的类型推断。

比如以往我们申明一个对象是这个样子的:

代码语言:javascript
复制
Dictionary<string, string> dictionary = new Dictionary<string, string>(); // without var keyword
var dictionary = new Dictionary<string, string>(); // with var keyword

但是在C#8中,将简化成这样:

代码语言:javascript
复制
class DictionaryWrapper
{
    private Dictionary<string, string> dictionary = new();
    // ...
}

Over:

当然距离C#8真是发布可能还要等一段时间,期间可能也会增加一些其他的特性,真正的体验效果还是一起期待8.0的发布吧

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-05-31 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.Nullable Reference Types
  • 2.Records
  • 3.Default Interface Methods
  • 4.Asynchronous Streams
  • 5.Ranges
  • 6.Generic Attributes
  • 7.Default Literal in Deconstruction
  • 8.Caller Argument Expression
  • 9.Target-typed new Expression
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档