Home >Backend Development >C#.Net Tutorial >What's new in C# 7.0 (quick preview)
"[Translation] New Features of C# 7" spends a lot of space introducing the 9 new features of C# 7.0. Here I will give a quick introduction to them through examples based on project experience, so that everyone can learn them in a short time. Learn about them within time.
In general, these new features make C# 7.0 easier to write code with functional programming ideas. C# 6.0 has done a lot of work on this road, and C# 7.0 is one step closer!
In C# 6.0, you can use Lambda expressions for member methods and read-only properties. The most frustrating thing at the time was why the set accessor of the property was not supported. Now, not only the set method supports Lambda expressions, but also constructors, destructors, and indexes can be defined in Lambda expressions.
class SomeModel { private string internalValue; public string Value { get => internalValue; set => internalValue = string.IsNullOrWhiteSpace(value) ? null : value; } }
out
Variablesout
Variables are syntax that existed before. C# 7.0 only allows it to declare and use it together to avoid One more line of code. The most direct effect is that two statements can be completed with one expression. Here is a simplified version of the Key
class as an example. This class was used by us early to process the ID value passed in through HTTP Get/Post.
public class Key { public string Value { get; } public Key(string key) { Value = key; } public int IntValue { get { // C# 6.0,需要提前定义 intValue,但不需要初始化 // 虽然 C# 6.0 可以为只读属性使用 Lambda 表达式 // 但这里无法用一个表达式表达出来 int intValue; return int.TryParse(Value, out intValue) ? intValue : 0; } } }
But it’s simple in C# 7
// 注意 out var intValue, // 对于可推导的类型甚至可以用 var 来申明变量 public int IntValue => int.TryParse(Value, out var intValue) ? intValue : 0;
Friends who have used System.Tuple
must be familiar with itItem1
, Item2
I am deeply offended by such meaningless naming. However, C# 7.0 brings semantic naming, and also simplifies the creation of tuples, no longer requiring Tuple.Create(...)
. In addition, to use the new tuple features and destructuring, you need to introduce the NuGet package System.ValueTuple
.
Install-Package System.ValueTuple
Of course, tuples are often used for methods that return multiple values. Some people also like to use out
parameters for returns, but even though out
variables are now possible, I still don't favor the widespread use of out
parameters.
The following example method is used to return a default time range (a total of 7 days starting from today) for data retrieval.
// 返回类型是一个包含两个元素的元组 (DateTime Begin, DateTime End) GetDefaultDateRange() { var end = DateTime.Today.AddDays(1); var begin = end.AddDays(-7); // 这里使用一对圆括号就创建了一个元组 return (begin, end); }
Call this method to get a tuple. Because the return value specifies the name of each data member when it is defined, obtaining data from the tuple can be semantic. Of course, you can still use Item1
and Item2
.
var range = GetDefaultDateRange(); var begin = range.Begin; // 也可以 begin = range.Item1 var end = range.End; // 也可以 end = range.Item2
The above example can be simplified without using the intermediate variable range
. This uses destructuring
var (begin, end) = GetDefaultDateRange();
The tuple created here is based on the return value. In fact, it is an expression and can create tuples anywhere. The logic of the above example is very simple and can be solved using expressions. The following example demonstrates a non-semantic return type declaration.
// 原来的 (DateTime Begin, DateTime End) 申明也是没问题的 (DateTime, DateTime) GetDefaultDateRange() => (DateTime.Today.AddDays(1).AddDays(-7), DateTime.Today.AddDays(1));
The destructuring method allows any class (not just tuples) to be destructured according to defined parameters. And the amazing thing is that the destructuring method can be a member method or defined as an extension method.
public class Size { public int Width { get; } public int Height { get; } public int Tall { get; } public Size(int width, int height, int tall) { this.Width = width; this.Height = height; this.Tall = tall; } // 定义成成员方法的解构 public void Deconstruct(out int width, out int height) { width = Width; height = Height; } } public static class SizeExt { // 定义成扩展方法的解构 public static void Deconstruct(this Size size, out int width, out int height, out int tall) { width = size.Width; height = size.Height; tall = size.Tall; } }
The following is the code using destructuring
var size = new Size(1920, 1080, 10); var (w, h) = size; var (x, y, z) = size;
Size
Remember that the construction method mentioned earlier can be defined as a Lambda expression Style? Here's a revamp of the Size constructor using tuples and Lambda - I'm already drunk!
public Size(int width, int height, int tall) => (Width, Height, Tall) = (width, height, tall);
Pattern matching currently supports is
and switch
. It sounds like a very lofty name, but to put it in a more down-to-earth way, it means judging the type and defining a specific type of reference. If you are interested, you can add some additional judgments.
For is
, it means defining a variable and then initializing it when making a judgment, so the code written like this
// 假设逻辑能保证这里的 v 可能是 string 也 可能是 int string ToString(object v) { if (v is int) { int n = (int) v; return n.ToString("X4"); } else { return (string) n; } }
can be simplified to——Okay , just write it as an expression in one step
string ToString(object v) => (v is int n) ? n.ToString("X4") : (string) v;
Of course you may say that the previous one can also be simplified into an expression - okay, let’s not delve into this issue, okay? I'm just demonstrating pattern matching of
is
.
And the pattern matching in switch
seems to be much more useful, let’s take ToString as an example
static string ToString(object v) { switch (v) { case int n when n > 0xffff: // 判断类型,匹配的情况下再对值进行一个判断 return n.ToString("X8"); case int n: // 判断类型,这里 n 肯定 <= 0xffff return n.ToString("X4"); case bool b: return b ? "ON" : "OFF"; case null: return null; default: return v.ToString(); } }
Pay attention to the first branch above## The usage of #when would be fine.
out and
ref can almost be avoided.
0b prefix, and the other is that it can be used in numerical literals. Use
_ arbitrarily to group numbers. This does not require a majority, just give two examples to understand.
const int MARK_THREE = 0b11; // 0x03 const int LONG_MARK = 0b_1111_1111; // 0xff const double PI = 3.14_1592_6536
经常写 JavaScript 的同学肯定会深有体会,局部函数是个好东西。当然它在 C# 中带来的最大好处是将某些代码组织在了一起。我之前在项目中大量使用了 Lambda 来代替局部函数,现在可以直接替换成局部函数了。Labmda 和局部函数虽然多数情况下能做同样的事情,但是它们仍然有一些区别
对于 Lambda,编译器要干的事情比较多。总之呢,就是编译效率要低得多
Lambda 通过委托实现,调用过程比较复杂,局部函数可以直接调用。简单地说就是局部函数执行效率更高
Lambda 必须先定义再使用,局部函数可以定义在使用之后。据说这在对递归算法的支持上会有区别
比较常用的地方是 Enumerator 函数和 async 函数中,因为它们实际都不是立即执行的。
我在项目中多是用来组织代码。局部函数代替只被某一个公共 API 调用的私有函数来组织代码虽然不失为一个简化类结构的好方法,但是把公共 API 函数的函数体拉长。所以很多时候我也会使用内部类来代替某些私有函数来组织代码。这里顺便说一句,我不赞成使用 #region
组织代码。
如果和 JavaScript 中 ES2017 的 async 相比,C# 中的 Task/Task<T> 就比较像 <code>Promise
的角色。不用羡慕 JavaScript 的 async 支持 Promise like,现在 C# 的 async 也支持 Task like 了,只要实现了 GetAwaiter
方法就行。
官方提供了一个 ValueTask
作为示例,可以通过 NuGet 引入:
Install-Package System.Threading.Tasks.Extensions
这个 ValueTask
比较有用的一点就是兼容了数据类型和 Task:
string cache; ValueTask<string> GetData() { return cache == null ? new ValueTask<string>(cache) : new ValueTask<string>(GetRemoteData()); // 局部函数 async Task<string> GetRemoteData() { await Task.Delay(100); return "hello async"; } }
"[Translation] New Features of C# 7" spends a lot of space introducing the 9 new features of C# 7.0. Here I will give a quick introduction to them through examples based on project experience, so that everyone can learn them in a short time. Learn about them within time.
In general, these new features make C# 7.0 easier to write code with functional programming ideas. C# 6.0 has done a lot of work on this road, and C# 7.0 is one step closer!
In C# 6.0, you can use Lambda expressions for member methods and read-only properties. The most frustrating thing at the time was why the set accessor of the property was not supported. Now, not only the set method supports Lambda expressions, but also constructors, destructors, and indexes can be defined in Lambda expressions.
class SomeModel { private string internalValue; public string Value { get => internalValue; set => internalValue = string.IsNullOrWhiteSpace(value) ? null : value; } }
out
Variablesout
Variables are syntax that existed before. C# 7.0 only allows it to declare and use it together to avoid One more line of code. The most direct effect is that two statements can be completed with one expression. Here is a simplified version of the Key
class as an example. This class was used by us early to process the ID value passed in through HTTP Get/Post.
public class Key { public string Value { get; } public Key(string key) { Value = key; } public int IntValue { get { // C# 6.0,需要提前定义 intValue,但不需要初始化 // 虽然 C# 6.0 可以为只读属性使用 Lambda 表达式 // 但这里无法用一个表达式表达出来 int intValue; return int.TryParse(Value, out intValue) ? intValue : 0; } } }
But it’s simple in C# 7
// 注意 out var intValue, // 对于可推导的类型甚至可以用 var 来申明变量 public int IntValue => int.TryParse(Value, out var intValue) ? intValue : 0;
Friends who have used System.Tuple
must be familiar with itItem1
, Item2
I am deeply offended by such meaningless naming. However, C# 7.0 brings semantic naming, and also simplifies the creation of tuples, no longer requiring Tuple.Create(...)
. In addition, to use the new tuple features and destructuring, you need to introduce the NuGet package System.ValueTuple
.
Install-Package System.ValueTuple
Of course, tuples are often used for methods that return multiple values. Some people also like to use out
parameters for returns, but even though out
variables are now possible, I still don't favor the widespread use of out
parameters.
The following example method is used to return a default time range (a total of 7 days starting from today) for data retrieval.
// 返回类型是一个包含两个元素的元组 (DateTime Begin, DateTime End) GetDefaultDateRange() { var end = DateTime.Today.AddDays(1); var begin = end.AddDays(-7); // 这里使用一对圆括号就创建了一个元组 return (begin, end); }
Call this method to get a tuple. Because the return value specifies the name of each data member when it is defined, obtaining data from the tuple can be semantic. Of course, you can still use Item1
and Item2
.
var range = GetDefaultDateRange(); var begin = range.Begin; // 也可以 begin = range.Item1 var end = range.End; // 也可以 end = range.Item2
The above example can be simplified without using the intermediate variable range
. This uses destructuring
var (begin, end) = GetDefaultDateRange();
The tuple created here is based on the return value. In fact, it is an expression and can create tuples anywhere. The logic of the above example is very simple and can be solved using expressions. The following example demonstrates a non-semantic return type declaration.
// 原来的 (DateTime Begin, DateTime End) 申明也是没问题的 (DateTime, DateTime) GetDefaultDateRange() => (DateTime.Today.AddDays(1).AddDays(-7), DateTime.Today.AddDays(1));
The destructuring method allows any class (not just tuples) to be destructured according to defined parameters. And the amazing thing is that the destructuring method can be a member method or defined as an extension method.
public class Size { public int Width { get; } public int Height { get; } public int Tall { get; } public Size(int width, int height, int tall) { this.Width = width; this.Height = height; this.Tall = tall; } // 定义成成员方法的解构 public void Deconstruct(out int width, out int height) { width = Width; height = Height; } } public static class SizeExt { // 定义成扩展方法的解构 public static void Deconstruct(this Size size, out int width, out int height, out int tall) { width = size.Width; height = size.Height; tall = size.Tall; } }
The following is the code using destructuring
var size = new Size(1920, 1080, 10); var (w, h) = size; var (x, y, z) = size;
Size
Remember that the construction method mentioned earlier can be defined as a Lambda expression Style? Here's a revamp of the Size constructor using tuples and Lambda - I'm already drunk!
public Size(int width, int height, int tall) => (Width, Height, Tall) = (width, height, tall);
Pattern matching currently supports is
and switch
. It sounds like a very lofty name, but to put it in a more down-to-earth way, it means judging the type and defining a specific type of reference. If you are interested, you can add some additional judgments.
For is
, it means defining a variable and then initializing it when making a judgment, so the code written like this
// 假设逻辑能保证这里的 v 可能是 string 也 可能是 int string ToString(object v) { if (v is int) { int n = (int) v; return n.ToString("X4"); } else { return (string) n; } }
can be simplified to——Okay , just write it as an expression in one step
string ToString(object v) => (v is int n) ? n.ToString("X4") : (string) v;
Of course you may say that the previous one can also be simplified into an expression - okay, let’s not delve into this issue, okay? I'm just demonstrating pattern matching of
is
.
And the pattern matching in switch
seems to be much more useful, let’s take ToString as an example
static string ToString(object v) { switch (v) { case int n when n > 0xffff: // 判断类型,匹配的情况下再对值进行一个判断 return n.ToString("X8"); case int n: // 判断类型,这里 n 肯定 <= 0xffff return n.ToString("X4"); case bool b: return b ? "ON" : "OFF"; case null: return null; default: return v.ToString(); } }
Pay attention to the first branch above## The usage of #when would be fine.
out and
ref can almost be avoided.
0b prefix, and the other is that it can be used in numerical literals. Use
_ arbitrarily to group numbers. This does not require a majority, just give two examples to understand.
const int MARK_THREE = 0b11; // 0x03 const int LONG_MARK = 0b_1111_1111; // 0xff const double PI = 3.14_1592_6536
经常写 JavaScript 的同学肯定会深有体会,局部函数是个好东西。当然它在 C# 中带来的最大好处是将某些代码组织在了一起。我之前在项目中大量使用了 Lambda 来代替局部函数,现在可以直接替换成局部函数了。Labmda 和局部函数虽然多数情况下能做同样的事情,但是它们仍然有一些区别
对于 Lambda,编译器要干的事情比较多。总之呢,就是编译效率要低得多
Lambda 通过委托实现,调用过程比较复杂,局部函数可以直接调用。简单地说就是局部函数执行效率更高
Lambda 必须先定义再使用,局部函数可以定义在使用之后。据说这在对递归算法的支持上会有区别
比较常用的地方是 Enumerator 函数和 async 函数中,因为它们实际都不是立即执行的。
我在项目中多是用来组织代码。局部函数代替只被某一个公共 API 调用的私有函数来组织代码虽然不失为一个简化类结构的好方法,但是把公共 API 函数的函数体拉长。所以很多时候我也会使用内部类来代替某些私有函数来组织代码。这里顺便说一句,我不赞成使用 #region
组织代码。
如果和 JavaScript 中 ES2017 的 async 相比,C# 中的 Task/Task<T> 就比较像 <code>Promise
的角色。不用羡慕 JavaScript 的 async 支持 Promise like,现在 C# 的 async 也支持 Task like 了,只要实现了 GetAwaiter
方法就行。
官方提供了一个 ValueTask
作为示例,可以通过 NuGet 引入:
Install-Package System.Threading.Tasks.Extensions
这个 ValueTask
比较有用的一点就是兼容了数据类型和 Task:
string cache; ValueTask<string> GetData() { return cache == null ? new ValueTask<string>(cache) : new ValueTask<string>(GetRemoteData()); // 局部函数 async Task<string> GetRemoteData() { await Task.Delay(100); return "hello async"; } }
The above is the detailed content of What's new in C# 7.0 (quick preview). For more information, please follow other related articles on the PHP Chinese website!