C#6.0新特性笔记
Getter专属赋值
可以在构造函数中,给只有get的属性赋初始值。
class Point
{
public int x { get; }
public Point()
{
x = 1;
}
}
自动属性初始化
可以给自动属性,赋初始化值
class Point
{
public int x { get; set; } = 1;
}
全局静态
可以设置全局静态,然后直接写静态类的方法。
using static System.Console;
namespace FxApp
{
internal class Program
{
private static void Main(string[] args)
{
Read();
}
}
}
全局枚举
可以设置全局枚举,然后直接写枚举属性
using static System.ConsoleColor;
namespace FxApp
{
internal class Program
{
private static void Main(string[] args)
{
var color = Red;
}
}
}
字符串插入
可以使用$来进行字符串插入,替代string.format。全局变量或参数都可插入
internal class Program
{
public static int x { get; set; }
private static void Main(string[] args)
{
string text = $"{x},{args}";
}
}
Expression bodied
可以使用箭头表达式来给方法或属性提供表达式体。注意,表达式体不能有括号。
internal class Program
{
public string Name => "Chenxy";
public int Sum(int x, int y) => x + y;
private static void Main(string[] args)
{
Program po = new Program();
Console.WriteLine(po.Name); //Chenxy
Console.WriteLine(po.Sum(1, 2)); //3
Console.Read();
}
}
索引值初始化
可以给集合赋初始化值
internal class Program
{
IDictionary<int, string> dict = new Dictionary<int, string>()
{
[1] = "first",
[2] = "second"
};
private static void Main(string[] args)
{
Program po = new Program();
foreach (var item in po.dict)
{
Console.WriteLine(item.Value);
}
Console.Read();
}
}
?.运算符
空值运算符,可以简化判断null的工作
public class Point
{
public int x { get; } = 1;
}
internal class Program
{
public static void Main(string[] args)
{
Point po = null;
//C# 5.0 写法
if (po != null)
{
Console.WriteLine(po.x);
}
//C# 6.0 写法
Console.WriteLine(po?.x);
Console.Read();
}
}
nameof表达式
为了防止重构时忘记修改,可以使用nameof来将一个常量和变量绑定。变量改变时,常量随之改变
internal class Program
{
public static void Main(string[] args)
{
string error = string.Empty;
//C# 5.0
Console.WriteLine("error"); //error
//C# 6.0
Console.WriteLine(nameof(error)); //error
Console.Read();
}
}
在此代码中,如果改变 error名称,但不修改nameof(error)名称。则会在编译时提示
异常过滤器
可以在try catch中进行判断,来控制是否进入catch。如果hideError为false,则不会进入catch。
public static void Main(string[] args)
{
bool hideError = false;
try
{
throw new Exception();
}
catch (Exception ex) when (hideError)
{
Console.WriteLine("异常已抛出");
}
Console.Read();
}
catch和finally中使用await
可以在catch 和 finally中,使用await。
internal class Program
{
public async static void Main(string[] args)
{
HttpClient client = null;
try
{
var result = await client.GetAsync("");
}
catch (Exception ex)
{
var result = await client.GetAsync("");
}
finally
{
var result = await client.GetAsync("");
}
Console.Read();
}
}
Visual Studio 2017
https://zhuanlan.zhihu.com/p/25626653
C#7.0新特性笔记
Out
原先代码中,如果使用Out参数,需要预先定义变量。而C#7中,则可以直接在输出参数中定义变量。
我们也可以直接在参数中,定义var隐式变量。
//VS2015
static void Main(string[] args)
{
int x;
OutMethod(out x);
WriteLine($"X:{x}");
Read();
}
//VS2017
static void Main(string[] args)
{
OutMethod(out int x);
WriteLine($"X:{x}");
Read();
}
解构
在调用函数中,可以将参数组合封装到一个类的构造函数中。但是无法通过对象解构成各个组成部分。
通过解构函数,则可以完成这个事情。解构函数可以返回对象的具体确定组件。
例如,我们模拟一个参数组合
public class PathInfo {
public string DirectoryName { get; set; }
public string FileName { get; set; }
public string Extension { get; set; }
public PathInfo(string dname, string fname, string extension)
{
DirectoryName = dname;
FileName = fname;
Extension = extension;
}
}
我们需要从nuget加载 System.ValueTuple这样,才可以使用元组进行解构。
我们如果要使用解构,就需要先定义一个解构函数:Desconstruct
public void Deconstruct(out string dname, out string fname, out string extension)
{
dname = DirectoryName;
fname = FileName;
extension = Extension;
}
需要注意的是,必须使用out,名称可以不一样,他是按照参数顺序返回的。
接下来,现在我们使用解构函数来调用。我们展示三种示例,来进行调用测试。
class Program
{
static void Main(string[] args)
{
PathInfo info = new PathInfo("d", "f", "e");
//示例一:参数内声明。也可以使用var
(string dname, string fname, string extension) = info;
WriteLine($"{dname},{fname},{extension}");
//示例二:声明值初始化。分配现有变量
string dname1, fname1, extension1 = null;
(dname1, fname1, extension1) = info;
WriteLine($"{dname1},{fname1},{extension1}");
//示例三:隐式变量声明
var (dname2, fname2, extension2) = info;
WriteLine($"{dname2},{fname2},{extension2}");
Read();
}
}
跟元组类似的写法,会给每个值进行赋值,只要顺序对应即可。
注意:在示例三种,我们在括号外面声明的var,这样括号内的三个变量类型必须一致。
解构函数,要求圆括号内至少有两个输出变量。如果只有一个是不起作用的,例如
(string dname) = info; //这样是不行的。Deconstruct,如果只有一个方法是不行的。
Deconstruct,可以进行重载。使用的时候,会自动找对应的重载方法。
元组
通过元组可以从一个方法中返回多个值。在C#7.0之前,我们会定义输出参数或者对象集合。
我们可以通过两种方式,来定义元祖。
1.不使用参数名,只填写参数类型。这样会返回 Item1,2,3
2.使用参数名,并填写参数类型。这样会返回Item.参数名
3.直接使用参数名。和2一个效果,但是方便一些。
class Program
{
static void Main(string[] args)
{
PathInfo info = new PathInfo();
var item = info.getEmpInfo();
WriteLine($"{item.Item1},{item.Item2},{item.Item3}");
var item2 = info.getEmpInfo2();
WriteLine($"{item2.a},{item2.b},{item2.c}");
var item3 = info.getEmpInfo3();
WriteLine($"{item3.a},{item3.b},{item3.c}");
Read();
}
}
public class PathInfo
{
// 不使用参数名,返回Item1,2,3
public (string, string, string) getEmpInfo()
{
return ("a", "b", "c");
}
// 使用参数名,返回定义参数名
public (string a, string b, string c) getEmpInfo2()
{
return ("a", "b", "c");
}
// 使用指定元素
public (string a, string b, string c) getEmpInfo3()
{
return (a: "a", b: "b", c: "c");
}
}
模式匹配
有时候基类会派生一些子类。我们如果要对每个类实施Eject方法。可以使用多个选择来实现。
AS运算符
使用AS运算符转换并赋值
检查结果是否为null
执行Eject操作
IS运算符
使用IS运算符检查类型
转换类型并为其赋值
执行Eject操作
Cast
显示转换赋值
捕捉可能的异常
执行操作
上述这些方法存在的问题都是语法相当冗长,总要为转换的类提供多个语句
具有IS表达式的模式匹配
C#7.0提供模式匹配,用作一种将测试和赋值合并为单个操作方法。
举个例子,我们在C#7以前,进行类型转换如下。我们使用IS运算符
class Program
{
static void Main(string[] args)
{
Eject(new Woman());
Read();
}
static void Eject(People peo)
{
if (peo is Woman)
{
Woman wo = (Woman)peo;
wo.Con();
}
}
}
class People {}
class Woman : People {
public void Con() {
WriteLine("This is Woman");
}
}
可以看出来,我们需要进行 is,然后进行转换。现在我们使用模式匹配来实现上面代码
static void Eject(People peo)
{
if (peo is Woman wo)
{
wo.Con();
}
}
模式匹配将测试和赋值合并成一个操作了。
Switch语句的模式匹配
使用Switch可以在多个可转换的兼容类型之间时更加重要。
如果我们想添加一个附加条件,可以使用when附加在case子句。
static void Eject(People peo)
{
switch (peo)
{
case Woman wo when wo != null:
wo.Con();
break;
case People pe:
break;
default:
break;
}
}
本地函数
C#7.0允许在一个成员内部完全声明本地函数。来替代Action和Func.
本地函数的作用于域,进在周围函数的内部。关于本地函数有以下几个注意点
1.不允许修饰符
2.不支持重载
3.本地函数可以访问所有变量,包括局部变量
4.本地函数存在整个方法范围内,声明不分前后
bool IsPalindrome(string text)
{
if (string.IsNullOrWhiteSpace(text)) return false;
bool LocalIsPalindrome(string target)
{
target = target.Trim(); // Start by removing any surrounding whitespace.
if (target.Length <= 1) return true;
else
{
return char.ToLower(target[0]) ==
char.ToLower(target[target.Length - 1]) &&
LocalIsPalindrome(
target.Substring(1, target.Length - 2));
}
}
return LocalIsPalindrome(text);
}
通过引用返回
通过ref可以将参数传递给函数来进行更新。
C#7.0除了ref参数外,还可以通过函数返回一个引用。
class Program
{
static void Main(string[] args)
{
int[] result = { 1, 2, 3 };
ref int a = ref Find(result);
a += 1;
WriteLine(result[0]);
Read();
}
static ref int Find(int[] sum)
{
return ref sum[0];
}
}
文本改进
C#7.0可以通过下划线来提高可读性。可以放在二进制、十进制、十六进制数字的任意位置
long LargestSquareNumberUsingAllDigits =
0b0010_0100_1000_1111_0110_1101_1100_0010_0100; // 9,814,072,356
long MaxInt64 { get; } =
9_223_372_036_854_775_807; // Equivalent to long.MaxValue
Throw表达式
可以在表达式中,直接抛出异常
public string getEmpInfo( string EmpName)
{
string[] empArr = EmpName.Split(",");
return (empArr.Length > 0) ? empArr[0] : throw new Exception("Emp Info Not exist");
}
异步返回类型
编译器不限制异步方法必须返回 void、Task、Task<T>。
任何可以继承 GetAwaiter 方法的都可以进行返回。
但是,此方法我验证不通过。故此暂时不考虑这个特性。
Expression bodied 增强
C#7.0里面把箭头函数表达式,进行了增强。
我们可以直接在构造函数、析构函数中使用
public class Point
{
public Point() => WriteLine("构造");
~Point() => WriteLine("释放");
}
$