Home >Backend Development >C#.Net Tutorial >What's new in C# 7.0 (quick preview)

What's new in C# 7.0 (quick preview)

巴扎黑
巴扎黑Original
2017-04-15 09:05:552065browse

"[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!

Expressions everywhere

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 Variables

out 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;

Tuples and destructuring

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));

Destructuring method Deconstrct

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;

The construction method of transformationSize

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

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.

ref local variables and ref return value

This is already a usage very close to C/C++. Although the official statement is that this can solve some security problems, I personally have not encountered its use scenarios so far. If the design is good enough, with the new features and deconstruction of tuples currently added, I personally think that the use of

out and ref can almost be avoided.

Since I haven’t used it, I won’t say much more. I’ll discuss it with classmates who have used it!

Numeric literal syntax enhancement

There are two enhancements here. One is the introduction of the binary digital syntax with the

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 组织代码。

支持更多 async 返回类型

如果和 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!

Expressions everywhere

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 Variables

out 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;

Tuples and destructuring

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));

Destructuring method Deconstrct

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;

The construction method of transformationSize

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

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.

ref local variables and ref return value

This is already a usage very close to C/C++. Although the official statement is that this can solve some security problems, I personally have not encountered its use scenarios so far. If the design is good enough, with the new features and deconstruction of tuples currently added, I personally think that the use of

out and ref can almost be avoided.

Since I haven’t used it, I won’t say much more. I’ll discuss it with classmates who have used it!

Numeric literal syntax enhancement

There are two enhancements here. One is the introduction of the binary digital syntax with the

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 组织代码。

支持更多 async 返回类型

如果和 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!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn