在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
虽然现在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.Recordsrecords是一个新的语法糖,它简化了原来创建简单类的过程,通过一条语句就可以创建出一个标准的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 StreamsC# 目前是已经支持了迭代器( 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的发布吧
|
请发表评论