>백엔드 개발 >C#.Net 튜토리얼 >C# 7.0의 새로운 기능(빠른 미리 보기)

C# 7.0의 새로운 기능(빠른 미리 보기)

巴扎黑
巴扎黑원래의
2017-04-15 09:05:552066검색

"[번역] C# 7의 새로운 기능"은 C# 7.0의 9가지 새로운 기능을 소개하는 데 많은 시간을 할애합니다. 여기서는 누구나 쉽게 배울 수 있도록 프로젝트 경험을 바탕으로 예제를 통해 빠르게 소개하겠습니다. 짧은 시간 안에 그들에 대해 알아보세요.

일반적으로 이러한 새로운 기능을 사용하면 C# 7.0의 함수형 프로그래밍 아이디어로 코드를 더 쉽게 작성할 수 있습니다. C# 6.0은 이 과정에서 많은 작업을 수행했으며 C# 7.0은 한 단계 더 가까워졌습니다!

모든 곳에서 표현식

C# 6.0에서는 멤버 메서드와 읽기 전용 속성에 대해 Lambda 표현식을 사용할 수 있습니다. 당시 가장 답답했던 점은 속성의 set 접근자가 지원되지 않는 이유였습니다. . 이제 set 메소드는 람다 표현식 사용을 지원할 뿐만 아니라 생성자, 소멸자 및 인덱스도 람다 표현식에서 정의할 수 있습니다.

class SomeModel
{
    private string internalValue;

    public string Value
    {
        get => internalValue;
        set => internalValue = string.IsNullOrWhiteSpace(value) ? null : value;
    }
}

out 변수

out 변수는 이전에 존재했던 구문입니다. C# 7.0에서는 코드 한 줄을 더 쓰지 않기 위해 선언과 사용을 함께 사용할 수 있습니다. 가장 직접적인 효과는 하나의 표현식으로 두 개의 명령문을 완성할 수 있다는 것입니다. 다음은 Key 클래스의 단순화된 버전입니다. 이 클래스는 HTTP Get/Post를 통해 전달된 ID 값을 처리하기 위해 초기에 사용되었습니다.

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

근데 C# 7에서는 간단해요

// 注意 out var intValue,
// 对于可推导的类型甚至可以用 var 来申明变量
public int IntValue => int.TryParse(Value, out var intValue) ? intValue : 0;

튜플과 구조분해

System.Tuple를 써본 친구들은 Item1Item2를 이렇게 생각해야 해요 무의미한 이름에 매우 불쾌감을 느낍니다. 그러나 C# 7.0에서는 의미 체계 명명 기능을 제공하는 동시에 튜플 생성을 줄여 Tuple.Create(...)가 필요하지 않습니다. 또한 새로운 튜플 기능과 구조 분해를 사용하려면 NuGet 패키지System.ValueTuple를 도입해야 합니다.

Install-Package System.ValueTuple

물론 튜플은 여러 값을 반환하는 메서드에 자주 사용됩니다. 어떤 사람들은 반환에 out 매개변수를 사용하는 것을 좋아하지만, 이제 out 변수가 가능하더라도 나는 여전히 out 매개변수가 널리 사용되는 것을 선호하지 않습니다.

다음 예시 메소드는 데이터 검색을 위한 기본 시간 범위(오늘부터 시작하여 총 7일)를 반환하는 데 사용됩니다.

// 返回类型是一个包含两个元素的元组
(DateTime Begin, DateTime End) GetDefaultDateRange()
{
    var end = DateTime.Today.AddDays(1);
    var begin = end.AddDays(-7);

    // 这里使用一对圆括号就创建了一个元组
    return (begin, end);
}

튜플을 가져오려면 이 메소드를 호출하세요. 반환 값은 정의될 때 각 데이터 멤버의 이름을 지정하기 때문에 튜플에서 데이터를 가져오는 것이 의미론적일 수 있습니다. 그리고 Item1. Item2

var range = GetDefaultDateRange();
var begin = range.Begin;    // 也可以 begin = range.Item1
var end = range.End;        // 也可以 end = range.Item2
위의 예는 중간 변수

를 사용하지 않고 단순화할 수 있습니다. 이는 구조 분해를 사용합니다. range

var (begin, end) = GetDefaultDateRange();
여기서 생성된 튜플은 실제로 반환 값으로 예시됩니다. 어디에서나 튜플을 만들 수 있습니다. 위 예제의 논리는 매우 간단하며 표현식을 사용하여 해결할 수 있습니다. 다음 예제에서는 의미 없는 반환 유형 선언을 보여줍니다.

// 原来的 (DateTime Begin, DateTime End) 申明也是没问题的
(DateTime, DateTime) GetDefaultDateRange()
    => (DateTime.Today.AddDays(1).AddDays(-7), DateTime.Today.AddDays(1));
구조 분해 방법 Deconstrct

구조 분해 방법을 사용하면 정의된 매개 변수에 따라 모든 클래스(튜플뿐만 아니라)를 구조 분해할 수 있습니다. 그리고 놀라운 점은 구조해제 메서드가 멤버 메서드일 수도 있고 확장 메서드로 정의될 수도 있다는 것입니다.

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;
    }
}
다음은 구조 분해

var size = new Size(1920, 1080, 10);
var (w, h) = size;
var (x, y, z) = size;
를 사용하여

Size

의 생성자를 변환하는 코드입니다. 앞서 언급한 생성자가 람다 표현식으로 정의될 수 있다는 점 기억하시나요? 다음은 튜플과 Lambda를 사용하여 Size 생성자를 개선한 것입니다. 저는 이미 취했습니다!

public Size(int width, int height, int tall)
    => (Width, Height, Tall) = (width, height, tall);
패턴 매칭

현재 패턴 매칭은

is을 지원합니다. 아주 거창한 이름처럼 들리지만, 좀 더 현실적으로 말하면 유형을 판단하고 특정 유형의 참조를 정의하는 것을 의미하며 관심이 있는 경우 몇 가지 추가 판단을 추가할 수 있습니다. switch

의 경우 판단할 때 변수를 정의하고 초기화하는 것이므로 is

// 假设逻辑能保证这里的 v 可能是 string 也 可能是 int
string ToString(object v) {
    if (v is int) {
        int n = (int) v;
        return n.ToString("X4");
    } else {
        return (string) n;
    }
}
와 같이 작성한 코드는 - 뭐, 한 단계만 단순화하면 됩니다. 표현식으로 작성

string ToString(object v)
    => (v is int n) ? n.ToString("X4") : (string) v;
물론 이전 표현식도 표현식으로 단순화할 수 있다고 말할 수 있습니다. 알았어, 이 문제는 깊이 파고들지 말자, 알았지? 저는 단지

에 대한 패턴 일치를 시연하고 있을 뿐입니다. is

의 패턴 일치가 훨씬 더 유용한 것 같습니다. ToString을 예로 들어보겠습니다. switch

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

위의 첫 번째 브랜치에서

의 사용법에 주의하세요. . 괜찮은. when

ref 지역 변수와 ref 반환 값

이미 C/C++에 매우 가까운 사용법입니다. 공식적으로는 이것이 일부 보안 문제를 해결할 수 있다고 나와 있지만 개인적으로 지금까지 이 방법을 사용하는 시나리오는 접해본 적이 없습니다. 디자인만 좋다면 현재 추가된 새로운 기능과 튜플의 해체로

, out의 사용은 거의 피할 수 있다고 개인적으로 생각합니다. ref

사용해본 적이 없어서 더 이상 말하지 않겠습니다.

숫자 리터럴 구문 개선

두 가지 개선 사항이 있습니다. 하나는

접두사가 있는 이진 숫자 리터럴 구문을 도입한 것이고, 다른 하나는 <를 임의로 사용할 수 있다는 것입니다. 🎜> 그룹 번호. 과반수가 필요하지 않습니다. 이해를 돕기 위해 두 가지 예만 제시하면 됩니다. 0b

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";
    }
}

                                               


"[번역] C# 7의 새로운 기능"은 C# 7.0의 9가지 새로운 기능을 소개하는 데 많은 시간을 할애합니다. 여기서는 누구나 쉽게 배울 수 있도록 프로젝트 경험을 바탕으로 예제를 통해 빠르게 소개하겠습니다. 짧은 시간 안에 그들에 대해 알아보세요.

일반적으로 이러한 새로운 기능을 사용하면 C# 7.0의 함수형 프로그래밍 아이디어로 코드를 더 쉽게 작성할 수 있습니다. C# 6.0은 이 과정에서 많은 작업을 수행했으며 C# 7.0은 한 단계 더 가까워졌습니다!

모든 곳에서 표현식

C# 6.0에서는 멤버 메서드와 읽기 전용 속성에 대해 Lambda 표현식을 사용할 수 있습니다. 당시 가장 답답했던 점은 속성의 set 접근자가 지원되지 않는 이유였습니다. . 이제 set 메소드는 람다 표현식 사용을 지원할 뿐만 아니라 생성자, 소멸자 및 인덱스도 람다 표현식에서 정의할 수 있습니다.

class SomeModel
{
    private string internalValue;

    public string Value
    {
        get => internalValue;
        set => internalValue = string.IsNullOrWhiteSpace(value) ? null : value;
    }
}

out 변수

out 변수는 이전에 존재했던 구문입니다. C# 7.0에서는 코드 한 줄을 더 쓰지 않기 위해 선언과 사용을 함께 사용할 수 있습니다. 가장 직접적인 효과는 하나의 표현식으로 두 개의 명령문을 완성할 수 있다는 것입니다. 다음은 Key 클래스의 단순화된 버전입니다. 이 클래스는 HTTP Get/Post를 통해 전달된 ID 값을 처리하기 위해 초기에 사용되었습니다.

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

근데 C# 7에서는 간단해요

// 注意 out var intValue,
// 对于可推导的类型甚至可以用 var 来申明变量
public int IntValue => int.TryParse(Value, out var intValue) ? intValue : 0;

튜플과 구조분해

System.Tuple를 써본 친구들은 Item1Item2를 이렇게 생각해야 해요 무의미한 이름에 매우 불쾌감을 느낍니다. 그러나 C# 7.0에서는 의미 체계 명명 기능을 제공하는 동시에 튜플 생성을 줄여 Tuple.Create(...)가 필요하지 않습니다. 또한 새로운 튜플 기능과 구조 분해를 사용하려면 NuGet 패키지System.ValueTuple를 도입해야 합니다.

Install-Package System.ValueTuple

물론 튜플은 여러 값을 반환하는 메서드에 자주 사용됩니다. 어떤 사람들은 반환에 out 매개변수를 사용하는 것을 좋아하지만, 이제 out 변수가 가능하더라도 나는 여전히 out 매개변수가 널리 사용되는 것을 선호하지 않습니다.

다음 예시 메소드는 데이터 검색을 위한 기본 시간 범위(오늘부터 시작하여 총 7일)를 반환하는 데 사용됩니다.

// 返回类型是一个包含两个元素的元组
(DateTime Begin, DateTime End) GetDefaultDateRange()
{
    var end = DateTime.Today.AddDays(1);
    var begin = end.AddDays(-7);

    // 这里使用一对圆括号就创建了一个元组
    return (begin, end);
}

튜플을 가져오려면 이 메소드를 호출하세요. 반환 값은 정의될 때 각 데이터 멤버의 이름을 지정하기 때문에 튜플에서 데이터를 가져오는 것이 의미론적일 수 있습니다. 그리고 Item1. Item2

var range = GetDefaultDateRange();
var begin = range.Begin;    // 也可以 begin = range.Item1
var end = range.End;        // 也可以 end = range.Item2
위의 예는 중간 변수

를 사용하지 않고 단순화할 수 있습니다. 이는 구조 분해를 사용합니다. range

var (begin, end) = GetDefaultDateRange();
여기서 생성된 튜플은 실제로 반환 값으로 예시됩니다. 어디에서나 튜플을 만들 수 있습니다. 위 예제의 논리는 매우 간단하며 표현식을 사용하여 해결할 수 있습니다. 다음 예제에서는 의미 없는 반환 유형 선언을 보여줍니다.

// 原来的 (DateTime Begin, DateTime End) 申明也是没问题的
(DateTime, DateTime) GetDefaultDateRange()
    => (DateTime.Today.AddDays(1).AddDays(-7), DateTime.Today.AddDays(1));
구조 분해 방법 Deconstrct

구조 분해 방법을 사용하면 정의된 매개 변수에 따라 모든 클래스(튜플뿐만 아니라)를 구조 분해할 수 있습니다. 그리고 놀라운 점은 구조해제 메서드가 멤버 메서드일 수도 있고 확장 메서드로 정의될 수도 있다는 것입니다.

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;
    }
}
다음은 구조 분해

var size = new Size(1920, 1080, 10);
var (w, h) = size;
var (x, y, z) = size;
를 사용하여

Size

의 생성자를 변환하는 코드입니다. 앞서 언급한 생성자가 람다 표현식으로 정의될 수 있다는 점 기억하시나요? 다음은 튜플과 Lambda를 사용하여 Size 생성자를 개선한 것입니다. 저는 이미 취했습니다!

public Size(int width, int height, int tall)
    => (Width, Height, Tall) = (width, height, tall);
패턴 매칭

현재 패턴 매칭은

is을 지원합니다. 아주 거창한 이름처럼 들리지만, 좀 더 현실적으로 말하면 유형을 판단하고 특정 유형의 참조를 정의하는 것을 의미하며 관심이 있는 경우 몇 가지 추가 판단을 추가할 수 있습니다. switch

의 경우 판단할 때 변수를 정의하고 초기화하는 것이므로 is

// 假设逻辑能保证这里的 v 可能是 string 也 可能是 int
string ToString(object v) {
    if (v is int) {
        int n = (int) v;
        return n.ToString("X4");
    } else {
        return (string) n;
    }
}
와 같이 작성한 코드는 - 뭐, 한 단계만 단순화하면 됩니다. 표현식으로 작성

string ToString(object v)
    => (v is int n) ? n.ToString("X4") : (string) v;
물론 이전 표현식도 표현식으로 단순화할 수 있다고 말할 수 있습니다. 알았어, 이 문제는 깊이 파고들지 말자, 알았지? 저는 단지

에 대한 패턴 일치를 시연하고 있을 뿐입니다. is

의 패턴 일치가 훨씬 더 유용한 것 같습니다. ToString을 예로 들어보겠습니다. switch

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

위의 첫 번째 브랜치에서

의 사용법에 주의하세요. . 괜찮은. when

ref 지역 변수와 ref 반환 값

이미 C/C++에 매우 가까운 사용법입니다. 공식적으로는 이것이 일부 보안 문제를 해결할 수 있다고 나와 있지만 개인적으로 지금까지 이 방법을 사용하는 시나리오는 접해본 적이 없습니다. 디자인만 좋다면 현재 추가된 새로운 기능과 튜플의 해체로

, out의 사용은 거의 피할 수 있다고 개인적으로 생각합니다. ref

사용해본 적이 없어서 더 이상 말하지 않겠습니다.

숫자 리터럴 구문 개선

두 가지 개선 사항이 있습니다. 하나는

접두사가 있는 이진 숫자 리터럴 구문을 도입한 것이고, 다른 하나는 <를 임의로 사용할 수 있다는 것입니다. 🎜> 그룹 번호. 과반수가 필요하지 않습니다. 이해를 돕기 위해 두 가지 예만 제시하면 됩니다. 0b

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";
    }
}



위 내용은 C# 7.0의 새로운 기능(빠른 미리 보기)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.