Always keep a beginner's|

石起起

园龄:1年10个月粉丝:1关注:0

2025-02-05 17:43阅读: 15评论: 0推荐: 0

C# 版本 7.0 新增特性

发布时间:2017 年 3 月

C# 7.0 版已与 Visual Studio 2017 一起发布。 此版本继承和发展了 C# 6.0。 以下介绍了部分新增功能:

其他功能包括:

所有这些功能都为开发者提供了新功能,帮助编写比以往任何时候都简洁的代码。 重点是缩减了使用 out 关键字的变量声明,并通过元组实现了多个返回值。 .NET Core 现在面向所有操作系统,着眼于云和可移植性。 语言设计者除了推出新功能外,也会在这些新功能方面付出时间和精力。


参考文章:
详解C#7.0新特性 - cnxy - 博客园
C#7.0 新增功能 - 张传宁 - 博客园


笔记

out 变量

if (int.TryParse(input, out int result))  // 使用内联式声明out变量,而不必提前声明
    Console.WriteLine(result);
else
    Console.WriteLine("Could not parse input");

// 也可以使用推断类型
if (int.TryParse(input, out var answer))
...

元组和析构函数

(string Alpha, string Beta) namedLetters = ("a", "b");  // 为元素中成员提供语义名称
Console.WriteLine($"{namedLetters.Alpha}, {namedLetters.Beta}");

var alphabetStart = (Alpha: "a", Beta: "b");  // 也可以在右侧指定字段名称
Console.WriteLine($"{alphabetStart.Alpha}, {alphabetStart.Beta}");

// 解构元组
(int max, int min) = Range(numbers); // 当方法返回值是无组类弄时,对返回值进行解包
Console.WriteLine(max);
Console.WriteLine(min);
// 为类添加析构函数(也叫解构器),关键字 Deconstruct
public class Point
{
   public double X { get; }
   public double Y { get; }
   
   public Point(double x, double y) => (X, Y) = (x, y);

   public void Deconstruct(out double x, out double y) => (x, y) = (X, Y);
}
var p = new Point(3.14, 2.71);  
(double X, double Y) = p;

模式匹配 is/switch/when

if (input is int count)  // 使用is进行模式匹配
    sum += count;
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:  // 附加了when 条件的类型模式
		sum += n;
		break;
	case null: // null 模式
		throw new NullReferenceException("Null found in sequence");
	default: // 默认事例
		throw new InvalidOperationException("Unrecognized type");
}

本地函数 (局部函数)

本地函数是一种嵌套在另一成员中的类型的方法。 仅能从其包含成员中调用它们。 可以在以下位置中声明和调用本地函数:

  • 方法(尤其是迭代器方法和异步方法)
  • 构造函数
  • 属性访问器
  • 事件访问器
  • 匿名方法
  • Lambda 表达式
  • 终结器
  • 其他本地函数

但是,不能在 expression-bodied 成员中声明本地函数。

private static string GetText(string path, string filename)
{
	var reader = File.OpenText($"{AppendPathSeparator(path)}{filename}");
	var text = reader.ReadToEnd();
	return text;

	// 👇 方法中的方法,也支持使用lambda写类似的函数
	string AppendPathSeparator(string filepath)
	{
		return filepath.EndsWith(@"\") ? filepath : filepath + @"\";
	}
}

已扩展 expression bodied 成员

允许表达式的成员进行了扩展:属性、构造函数、析构函数、get 和 set 访问器、索引器

public class ExpressionMembersExample
{
	// Expression-bodied 构造函
	public ExpressionMembersExample(string label) => this.Label = label;

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

	private string label;

	// Expression-bodied get / set 
	public string Label
	{
		get => label;
		set => this.label = value ?? "Default label";
	}
	
	// Expression-bodied indexers
	public string this[string name] => Convert.ToBase64String(Encoding.UTF8.GetBytes(name));
}

ref 局部变量

声明局部变量并在变量类型之前添加 ref 关键字时,即声明了 reference 变量,或称 ref 局部变量。

ref int aliasOfvariable = ref variable;
// 可以理解为指向引用的引用,但它们引用的数据都是同一个堆上的数据

reference 变量是引用另一个变量(称为引用)的变量。也就是说,reference 变量是其引用的另名。向 reference 变量赋值时,该值将分配给引用。读取 reference 变量的值时,将返回引用的值。

引用返回

默认情况下, return 语句返回表达式的值。以返回对变量的引用。 引用返回值(或 ref 返回值)是由方法按引用向调用方返回的值。 即是说,调用方可以修改方法所返回的值,此更改反映在所调用方法中的对象的状态中。 为此,请使用带 ref 关键字的 return 语句,如以下示例所示:

int[] xs = new int [] {10, 20, 30, 40 };
ref int found = ref FindFirst(xs, s => s == 30);
found = 0;
Console.WriteLine(string.Join(" ", xs));  // output: 10 20 0 40

ref int FindFirst(int[] numbers, Func<int, bool> predicate)
{
    for (int i = 0; i < numbers.Length; i++)
    {
        if (predicate(numbers[i]))
        {
            return ref numbers[i];
        }
    }
    throw new InvalidOperationException("No element satisfies the given condition.");
}

❓ 难道平时使用返回的值是一个副本?与返回的原值不是堆上的同一个数据吗?

弃元

(_, _, area) = city.GetCityInformation(cityName);
var (_, _, _, pop1, _, pop2) = QueryCityDataForYears("New York City", 1960, 2010);
var (fName, _, city, _) = p; // 解构
static void ProvidesFormatInfo(object? obj) => Console.WriteLine(obj switch { IFormatProvider fmt => $"{fmt.GetType()} object", 
null => "A null object reference: Its use could result in a NullReferenceException", 
_ => "Some object type without format information" 
});

// 👆 这种写法太超前,不懂❓

二进制文本和数字分隔符

整数文本可以是

  • 十进制:不使用任何前缀
  • 十六进制:使用 0x 或 0X 前缀
  • 二进制:使用 0b 或 0B 前缀

下面的代码演示每种类型的示例:

var decimalLiteral = 42;
var hexLiteral = 0x2A;
var binaryLiteral = 0b_0010_1010;

前面的示例还演示了如何将 _ 用作数字分隔符。 可以将数字分隔符用于所有类型的数字文本。

二次解读:

var one = 0b0001;
var sixteen = 0b0001_0000;

long salary = 1000_000_000;
decimal pi = 3.141_592_653_589m;

注:二进制文本是以0b(零b)开头,字母不区分大小写;数字分隔符只有三个地方不能写:开头,结尾,小数点前后。

总结:二进制文本,数字分隔符 可使常量值更具可读性。

引发表达式

throw 表达式

还可以将 throw 用作表达式。 这在很多情况下可能很方便,包括:

string first = args.Length >= 1 
	? args[0]
	: throw new ArgumentException("Please supply at least one argument.");
public string Name
{
	get => name;
	set => name = value ??
		throw new ArgumentNullException(paramName: nameof(value), message: "Name cannot be null");
}
DateTime ToDateTime(IFormatProvider provider) =>
		 throw new InvalidCastException("Conversion to a DateTime is not supported.");

其它 ValueTask<int>

从异步方法返回 Task 对象可能在某些路径中导致性能瓶颈。 Task 是引用类型,因此使用它意味着分配对象。 如果使用 async 修饰符声明的方法返回缓存结果或以同步方式完成,那么额外的分配在代码的性能关键部分可能要耗费相当长的时间。 如果这些分配发生在紧凑循环中,则成本会变高。

新语言功能意味着异步方法返回类型不限于 TaskTask<T> 和 void。 返回类型必须仍满足异步模式,这意味着 GetAwaiter 方法必须是可访问的。 作为一个具体示例,已将 ValueTask 类型添加到 .NET framework 中,以使用这一新语言功能:

 public async ValueTask<int> Func()
 {
    await Task.Delay(100);
    return 5;
 }

本文作者:石起起

本文链接:https://www.cnblogs.com/myshiqiqi/p/18699884

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   石起起  阅读(15)  评论(0编辑  收藏  举报
评论
收藏
关注
推荐
深色
回顶
收起
点击右上角即可分享
微信分享提示