官方文档为 :C#6.0新特性,需要学习的可以直接去看官方文档,下面是我对6.0的学习笔记。
C#6.0
只读属性
public string Test1 { get; }
当设置该属性仅读类型,只允许在类的构造函数中赋值。其他地方仅允许取值。
class Program
{
public string Test1 { get; }
public Program()
{
Test1 = "1";//不允许赋值
}
}
有些类似于readonly类型,如下
public static readonly string testReadOnly="b";
自动属性初始化表达式
语法糖,可以在属性后直接给对象赋值
public string Test2 { get; set; } = "b";
注意只有自动属性才可以赋值,也就是使用{ get; set; }
而不是
get
{
return Test1;
}
set
{
if (value=="a")
{
Test1 = value+"b";
};
} //= "a"; 此时再赋值会提示只有自动实现的属性才具有初始值设定项
官方文档给出的例子是(list继承于ICollection)
public ICollection<double> Grades { get; } = new List<double>();
Expression-bodied 函数成员
之前版本的重写ToString方法为
public override string ToString()
{
return base.ToString();
}
现在使用表达式直接写成
public override string ToString() => $"{LastName}, {FirstName}";
这个$是C#6.0的新特性字符串内插,下面也会介绍到,这个表达式主体特性只能使用于方法和只读属性。个人觉得如果方法是复杂处理的话这种写法很容易乱,还不如直接写。属性还比较好一点。
表达式主体用法针对于方法的例子如下
using System;
public class Person
{
public Person(string firstName, string lastName)
{
fname = firstName;
lname = lastName;
}
private string fname;
private string lname;
public override string ToString() => $"{fname} {lname}".Trim();
public void DisplayName() => Console.WriteLine(ToString());
}
class Example
{
static void Main()
{
Person p = new Person("Mandy", "Dejesus");
Console.WriteLine(p);
p.DisplayName();
}
}
表达式主体用法针对于属性的例子如下
public class Location
{
private string locationName;
public Location(string name)
{
locationName = name;
}
public string Name => locationName;
}
个人的理解为
public string Name => locationName;
类似于,给元素赋一个只读的属性
public string Name { get { return locationName; } }
using static
直接可以在类中直接使用方法,例如
String.Compare();
当声明using static System.String;
后
可以直接写成
Compare()
省的敲String了,但是需要注意引用时需要提供完全限定的类型名称(完整的命名空间名称以及类型名称),也就是using static System.String
而不是using static String
,但是你引用错了编译器也会抛异常提示。所以可以放心的using。
Null 条件运算符
这个记得在之前的C#版本中有一个语法糖,值类型不允许为空,这个特性我经常使用int? a = null;
,这样值类型就允许为空了。
而NULL条件运算符为?.
var first = person?.FirstName;
官方文档解释很清楚 :如果 Person 对象是 null,则将变量 first 赋值为 null。 否则,将 FirstName 属性的值分配给该变量。 最重要的是,?. 意味着当 person 变量为 null 时,此行代码不会生成 NullReferenceException。 它会短路并返回 null。 还可以将 null 条件运算符用于数组或索引器访问。
person 的值是什么,以下表达式均返回 string。
first = person?.FirstName ?? "Unspecified";
上面这个代码很实用,之前很多行的代码一行就可以代替。就是判断?.左侧是否为空,为空就返回null。
字符串内插
非常喜欢的新特性,之前拼接string都是string.Format("{0},{1}", "s", "a")
这种类似写法,现在可以无限的拼接字符串,并且没有限制。
官方说明 : 新的字符串内插功能可以在字符串中嵌入表达式。 使用 $ 作为字符串的开头,并使用 { 和 } 之间的表达式代替序号
public string FullName => $"{FirstName} {LastName}";
下面这种方式使用上面的Expression-bodied加字符串内插
public string GetGradePointPercentage() =>
$"Name: {LastName}, {FirstName}. G.P.A: {Grades.Average():F2}";
异常筛选器
这个也是很喜欢的新特性,try catch 的时候不好去判断Exception的类型,却想针对异常去分别处理。之前我用的方法都是判断Exception ex,将ex.tostring()。后判断是否包含异常。
官方文档为*“异常筛选器”是确定何时应该应用给定的 catch 子句的子句 。 如果用于异常筛选器的表达式计算结果为 true,则 catch 子句将对异常执行正常处理。 如果表达式计算结果为 false,则将跳过 catch 子句。 一种用途是检查有关异常的信息,以确定 catch 子句是否可以处理该异常:*
try
{
}
catch(System.Net.Http.HttpRequestException e)
{
}
catch (Exception ex) when (ex.Message.Contains(""))
{
throw;
}
nameof 表达式
官方文档:nameof 表达式的计算结果为符号的名称。 每当需要变量、属性或成员字段的名称时,这是让工具正常运行的好办法。 nameof 的其中一个最常见的用途是提供引起异常的符号的名称:
需要变量名称时,旧版本可以考虑反射,现在直接用nameof(lastName)
,就可以直接获取到变量名称
Console.WriteLine(nameof(System.Collections.Generic)); // output: Generic
Console.WriteLine(nameof(List<int>)); // output: List
Console.WriteLine(nameof(List<int>.Count)); // output: Count
Console.WriteLine(nameof(List<int>.Add)); // output: Add
var numbers = new List<int> { 1, 2, 3 };
Console.WriteLine(nameof(numbers)); // output: numbers
Console.WriteLine(nameof(numbers.Count)); // output: Count
Console.WriteLine(nameof(numbers.Add)); // output: Add
Catch 和 Finally 块中的 Await
官方文档:在 catch 和 finally 子句中添加 await 支持的实现细节可确保该行为与同步代码的行为一致。 当在 catch 或 finally 子句中执行的代码引发异常时,执行将在下一个外层块中查找合适的 catch 子句。 如果存在当前异常,则该异常将丢失。 catch 和 finally 子句中的 awaited 表达式也会发生同样的情况:搜索合适的 catch,并且当前异常(如果有)将丢失。
主要是用在多线程异步中,之前版本,线程中异常会跑到外层块的catch,使用这个可以让日志记录等操作与代码编写的同步。这个个人觉得异步中使用不会太大。线程异常外部记录也是一样会记录异常信息以及代码异常的位置。
使用索引器初始化关联集合
官方文档说:可以将集合初始值设定项与 Dictionary<TKey,TValue> 集合和其他类型一起使用,看的我以为可以像python一样,不区分类型添加集合的数据,但实际不是,只是支持了可以使用符号 **[]**去包裹键,取值也支持用
private static Dictionary<int, string> web = new Dictionary<int, string>
{
[404] = "Page not Found",
[302] = "Page moved, but left a forwarding address.",
[500] = "The web server can't come out to play today."
};
web[404]
有区别吗? 我觉得并没有?
改进了重载解析
这个只是对编译器的优化,理解下就好,官方说明如下
以前版本的 C# 编译器可能会发现涉及 lambda 表达式的一些方法不明确。 请考虑此方法:
static Task DoThings()
{
return Task.FromResult(0);
}
在早期版本的 C# 中,使用方法组语法调用该方法将失败:
Task.Run(DoThings);
早期的编译器无法正确区分 Task.Run(Action)
和 Task.Run(Func<Task>())
。 在早期版本中,需要使用 lambda 表达式作为参数:
Task.Run(() => DoThings());
C# 6 编译器正确地确定 Task.Run(Func<Task>())
是更好的选择。