目录
- C#7
C#7
C#7.0新特性
C# 7.3在2017年3月
发布
out 变量(out variables)
以前我们使用out变量必须在使用前进行声明,C# 7.0 给我们提供了一种更简洁的语法 “使用时进行内联声明” 。如下所示:
var input = ReadLine();
if(int.TryParse(input, out var result))
{
WriteLine("您输入的数字是:{0}", result);
}
else
{
WriteLine("无法解析输入...");
}
等价于以前的
int num;
string s = Console.ReadLine();
if(int.TryParse(s, out num))
{
Console.WriteLine("您输入的数字是:{0}", num);
}
else
{
Console.WriteLine("无法解析输入...");
}
原理解析:所谓的 “内联声明” 编译后就是以前的原始写法,只是现在由编译器来完成。
备注:在进行内联声明时,即可直接写明变量的类型也可以写隐式类型,因为out关键字修饰的一定是局部变量。
元组(Tuples)
元组(Tuple)在 .Net 4.0 的时候就有了,但元组也有些缺点,如:
1)Tuple 会影响代码的可读性,因为它的属性名都是:Item1,Item2.. 。
2)Tuple 还不够轻量级,因为它是引用类型(Class)。
备注:上述所指 Tuple 还不够轻量级,是从某种意义上来说的或者是一种假设,即假设分配操作非常的多。
C# 7 中的元组(ValueTuple)解决了上述两个缺点:
1)ValueTuple 支持语义上的字段命名。
2)ValueTuple 是值类型(Struct)。
如何创建一个元组
var tuple = (1, 2); // 使用语法糖创建元组
var tuple2 = ValueTuple.Create(1, 2); // 使用静态方法【Create】创建元组
var tuple3 = new ValueTuple&<int, int>(1, 2); // 使用 new 运算符创建元组
WriteLine($"first:{tuple.Item1}, second:{tuple.Item2}, 上面三种方式都是等价的。");
原理解析:上面三种方式最终都是使用 new 运算符来创建实例。
如何创建给字段命名的元组
// 左边指定字段名称
(int one, int two) tuple = (1, 2);
WriteLine($ "first:{tuple.one}, second:{tuple.two}");
// 右边指定字段名称
var tuple2 = (one: 1, two: 2);
WriteLine($ "first:{tuple2.one}, second:{tuple2.two}");
// 左右两边同时指定字段名称
(int one, int two) tuple3 = (first: 1, second: 2); /* 此处会有警告:由于目标类型(xx)已指定了其它名称,因为忽略元组名称xxx */
WriteLine($ "first:{tuple3.one}, second:{tuple3.two}");
注:左右两边同时指定字段名称,会使用左边的字段名称覆盖右边的字段名称(一一对应)。
原理解析:上述给字段命名的元组在编译后其字段名称还是:Item1, Item2...,即:“命名”只是语义上的命名。
元组的使用
var (one, two) = GetTuple();
WriteLine($"first:{one}, second:{two}");
static (int, int) GetTuple()
{
return (1, 2);
}
is 表达式(is expressions)
static int GetSum(IEnumerable<object> values)
{
var sum = 0;
if(values == null) return sum;
foreach(var item in values)
{
if(item is short) // C# 7 之前的 is expressions
{
sum += (short) item;
}
else if(item is int val) // C# 7 的 is expressions
{
sum += val;
}
else if(item is string str && int.TryParse(str, out var result)) // is expressions 和 out variables 结合使用
{
sum += result;
}
else if(item is IEnumerable<object> subList)
{
sum += GetSum(subList);
}
}
return sum;
}
原理解析:此 is 非彼 is ,这个扩展的 is 其实是 as 和 if 的组合。即它先进行 as 转换再进行 if 判断,判断其结果是否为 null,不等于 null 则执行
语句块逻辑,反之不行。由上可知其实C# 7之前我们也可实现类似的功能,只是写法上比较繁琐。
switch语句更新(switch statement updates)
基于类型的模式匹配
Circle Rectangle是自己创建的类
public void ProcessShape(object shape)
{
switch (shape)
{
case Circle c:
Console.WriteLine($"Processing Circle with radius {c.Radius}");
break;
case Rectangle r:
Console.WriteLine($"Processing Rectangle with width {r.Width} and height {r.Height}");
break;
default:
Console.WriteLine("Unknown shape");
break;
}
}
基于条件的模式匹配
public void ProcessNumber(int number)
{
switch (number)
{
case int i when (i < 0):
Console.WriteLine("Negative number");
break;
case int i when (i > 0):
Console.WriteLine("Positive number");
break;
default:
Console.WriteLine("Zero");
break;
}
}
局部引用和引用返回 (Ref locals and returns)
我们知道 C# 的 ref 和 out 关键字是对值传递的一个补充,是为了防止值类型大对象在Copy过程中损失更多的性能。现在在C# 7中 ref 关键字得
到了加强,它不仅可以获取值类型的引用而且还可以获取某个变量(引用类型)的局部引用。如:
static ref int GetLocalRef(int[, ] arr,Func<int, bool>func)
{
for(int i = 0; i < arr.GetLength(0); i++)
{
for(int j = 0; j < arr.GetLength(1); j++)
{
if(func(arr[i, j]))
{
return ref arr[i, j];
}
}
}
throw new InvalidOperationException("Not found");
}
Call:
int[,] arr = { { 10, 15 }, { 20, 25 } };
ref var num = ref GetLocalRef(arr, c => c == 20);
num = 600;
Console.WriteLine(arr[1, 0]);
Print results:
使用方法:
1. 方法的返回值必须是引用返回:
a) 声明方法签名时必须在返回类型前加上 ref 修饰。
b) 在每个 return 关键字后也要加上 ref 修饰,以表明是返回引用。
2. 分配引用(即赋值),必须在声明局部变量前加上 ref 修饰,以及在方法返回引用前加上 ref 修饰。
注:C# 开发的是托管代码,所以一般不希望程序员去操作指针。并由上述可知在使用过程中需要大量的使用 ref 来标明这是引用变量(编译后其
实没那么多),当然这也是为了提高代码的可读性。
局部函数(Local functions)
C# 7 中的一个功能“局部函数”,如下所示:
public void GlobalToLocalExample()
{
string[] words = {"the", "quick", "brown", "fox", "jumps"};
//局部函数定义
string ConvertArrayToString()
{
string result = string.Empty;
for (var i = 0; i < words.Length; i++)
{
result += words[i];
if (i != words.Length - 1)
{
result += " ";
}
}
return result;
}
//调用局部函数
string sentence = ConvertArrayToString();
Console.WriteLine(sentence); //输出 "the quick brown fox jumps"
}
更多的表达式体成员(More expression-bodied members)
C# 6 的时候就支持表达式体成员,但当时只支持“函数成员”和“只读属性”,这一特性在C# 7中得到了扩展,它能支持更多的成员:构造函数、析构函数、带 get,set 访问器的属性、以及索引器。如下所示:
public class ExampleClass
{
private string _name;
// 构造函数
public ExampleClass(string name) => _name = name ?? throw new ArgumentNullException(nameof(name));
// 属性访问器
public string Name
{
get => _name;
set => _name = value ?? throw new ArgumentNullException(nameof(value));
}
// 析构函数
~ExampleClass() => Console.WriteLine("Destructed");
}
备注:索引器其实在C# 6中就得到了支持,但其它三种在C# 6中未得到支持。
Throw 表达式(Throw expressions)
Lambda表达式中的throw
Func<int, int> function = x => x >= 0 ? x : throw new ArgumentException("x cannot be negative");
属性设置器中的throw
public class Student
{
private string _name = GetName() ?? throw new ArgumentNullException(nameof(GetName));
private int _age;
public int Age
{
get => _age;
set => _age = value <= 0 || value >= 130 ? throw new ArgumentException("参数不合法") : value;
}
static string GetName() => null;
}
扩展异步返回类型(Generalized async return types)
以前异步的返回类型必须是:Task、Task
public async ValueTask<int>Func()
{
await Task.Delay(3000);
return 100;
}
数字文本语法的改进
C# 7 还包含两个新特性:二进制文字、数字分隔符,如下所示:
int binaryRepresentation = 0b1101;
long billGatesNetWorth = 122_000_000_000;
double avogadroConstant = 6.022_140_76e23;
int flagBits = 0b1010_1011_1100_1101_1110_1111;
注:二进制文本是以0b(零b)开头,字母不区分大小写;数字分隔符只有三个地方不能写:开头,结尾,小数点前后。
C#7.1新特性
C# 7.3在2017年8月
发布
默认文本表达式
无需指定类型即可使用 default 关键字。编译器可以通过上下文自动推断其类型,例如:
int i = default; // Equivalent to "int i = default(int);"
异步main方法
Main 方法可以被定义为异步(async)。这允许在 Main 方法中直接调用异步方法,如下例所示:
static async Task Main(string[] args)
{
await ProcessDataAsync();
}
推断元组元素名称
var count = 5;
var label = "Colors used in the map";
var pair = (count, label); // element names are "count" and "label"
泛型类型参数的模式匹配
public static void Print<T>(T obj)
{
if (obj is string str)
{
Console.WriteLine($"'{obj}' is a string: {str}");
}
else
{
Console.WriteLine($"{obj} is not a string.");
}
}
引用程序集生成
C#7.2新特性
C# 7.3在2017年11月
发布
引用语义上的只读结构体
C# 7.2 允许开发者声明只读结构体,这些结构体在作为只读变量,只读字段或只读索引器返回值时不会被复制。
例如:
public readonly struct ReadOnlyPoint
{
public ReadOnlyPoint(double x, double y)
{
X = x;
Y = y;
}
public double X { get; }
public double Y { get; }
}
private protected 访问修饰符
private protected
访问修饰符允许一个成员被其定义类型的子类型所访问,前提是这些子类型在同一组件中声明。
例如:
public class BaseClass
{
private protected int myValue = 0;
}
public class DerivedClass1 : BaseClass
{
void Access()
{
var baseObject = new BaseClass();
baseObject.myValue = 5; // 错误
myValue = 5; // 正确
}
}
非尾部命名参数
C# 7.2 允许开发者在调用方法时在命名参数之后传递位置参数。
例如:
public void ExampleMethod(int required, string optionalstr = "default", int optionalint = 10)
{
//...
}
ExampleMethod(3, optionalint:4, "non-trailing"); //正确
in 参数修饰符
C# 7.2 引入了 in
参数修饰符,以便通过引用传递参数,而不是通过值传递,这样可以减少大型结构体的复制操作来提高性能。in
修饰符不仅表明一个参数作为引用传递,而且该参数在方法中是只读的,不能被修改。
下面是使用 in
参数修饰符的一个示例:
public struct Point
{
public double X { get; }
public double Y { get; }
public Point(double x, double y)
{
X = x;
Y = y;
}
public double Distance(in Point other)
{
double xDifference = X - other.X;
double yDifference = Y - other.Y;
return Math.Sqrt(xDifference * xDifference + yDifference * yDifference);
}
}
public class Test
{
public void TestMethod()
{
Point a = new Point(0, 0);
Point b = new Point(3, 4);
Console.WriteLine(a.Distance(in b)); // 输出 5
}
}
在这个示例中,Distance
方法接受一个 in
修饰的 Point
参数 other
,这样当我们传递 b
给 Distance
方法时,b
不会被复制,而是作为引用传递。这将避免对大型结构的复制,从而提高了性能。
需要注意的是,in
参数在方法内部是只读的,你不能在方法体内修改它。例如,如果你尝试运行 other.X = 10;
,编译器会报错。
ref readonly 结构
ref readonly
是一个新引入的特性,这个特性允许你返回方法的只读引用。这对于那些不希望被修改的大型结构或数组非常有用,因为你可以返回对它们的引用,而不是复制它们。这可以提高应用的性能。
此外,ref readonly
还可以与 in
修饰符一起使用,以便按引用传递只读参数。
下面是一个使用 ref readonly
的示例:
public struct LargeStruct
{
public int One { get; }
public int Two { get; }
public int Three { get; }
// 依此类推...
}
public class Container
{
private LargeStruct largeStruct;
public ref readonly LargeStruct GetStruct()
{
return ref largeStruct;
}
}
public class Test
{
public void TestMethod()
{
Container container = new Container();
ref readonly LargeStruct largeStructRef = ref container.GetStruct();
// largeStructRef.One = 10; // 这里会报错,因为largeStructRef 是只读引用
int one = largeStructRef.One; // 这是可以的
}
}
C#7.3新特性
C# 7.3在2018年5月
发布
ref局部变量
现在可以使用ref关键字声明局部变量。
示例如下:
public void MyMethod()
{
int number = 5;
ref int refToLocal = ref number;
refToLocal = 10;
Console.WriteLine(number); // 输出 "10"
}
委托约束
现在可以在泛型代码中指定委托和System.Enum
作为约束。
示例如下:
public class MyGenericClass<T> where T : Delegate
{
public void SomeMethod(T delegateInstance)
{
delegateInstance.DynamicInvoke();
}
}
元组等式的比较和相等性
元组类型可以适用于==和!=比较运算符。
示例如下:
var tuple1 = (5, 10);
var tuple2 = (5, 10);
Console.WriteLine(tuple1 == tuple2); // 输出 "True"
扩展初始值设定项中的表达式变量
表达式变量现在可以在更多的上下文中使用。
示例如下:
public class B
{
public B(int i, out int j)
{
j = i;
}
}
public class D : B
{
public D(int i) : base(i, out var j)
{
Console.WriteLine($"The value of 'j' is {j}");
}
}
引用传参的重载解析
这是解决函数重载解析中的一些模糊性的改进。
示例如下:
void Method(object obj)
{
Console.WriteLine("object");
}
void Method(int i)
{
Console.WriteLine("int");
}
void Caller()
{
object testObj = 1;
Method(testObj); // 输出 "object",而不是 "int"
Method((int)testObj); // 输出 "int"
}
unmanaged约束
public unsafe int SizeOf<T>() where T : unmanaged
{
return sizeof(T);
}
标签:C#,特性,元组,int,7.0,WriteLine,ref,public
From: https://www.cnblogs.com/shilinfeng/p/18084649