Maison  >  Article  >  développement back-end  >  Quoi de neuf dans C# 7.0 (aperçu rapide)

Quoi de neuf dans C# 7.0 (aperçu rapide)

巴扎黑
巴扎黑original
2017-04-15 09:05:551985parcourir

"[Traduction] Nouvelles fonctionnalités de C# 7" consacre beaucoup d'espace à la présentation des 9 nouvelles fonctionnalités de C# 7.0. Je vais ici les présenter rapidement à travers des exemples basés sur l'expérience du projet, afin que chacun puisse les apprendre. un court laps de temps. Apprenez-en davantage à leur sujet dans le temps.

En général, ces nouvelles fonctionnalités facilitent l'écriture de code avec des idées de programmation fonctionnelle en C# 7.0. C# 6.0 a fait beaucoup de travail sur cette voie, et C# 7.0 est un pas de plus !

Expression partout

En C# 6.0, vous pouvez utiliser des expressions Lambda pour les méthodes membres et les propriétés en lecture seule. La chose la plus frustrante à l'époque était la raison pour laquelle l'accesseur défini de la propriété n'était pas pris en charge. . Désormais, non seulement la méthode set prend en charge les expressions Lambda, mais les constructeurs, destructeurs et index peuvent également être définis dans les expressions Lambda.

class SomeModel
{
    private string internalValue;

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

out Variables

out Les variables sont une syntaxe qui existait auparavant. C# 7.0 lui permet simplement de regrouper la déclaration et l'utilisation pour éviter une ligne de code supplémentaire. L’effet le plus direct est que deux énoncés peuvent être complétés par une seule expression. Voici une version simplifiée de la classe Key à titre d'exemple. Cette classe a été utilisée par nous au début pour traiter les valeurs d'ID transmises via 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;
        }
    }
}

Mais c'est simple en C# 7

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

Tuple et déstructuration

Les amis qui ont utilisé System.Tuple doivent le connaîtreItem1, Item2 Je suis profondément offensé par une dénomination aussi dénuée de sens. Cependant, C# 7.0 apporte une dénomination sémantique et réduit en même temps la création de tuples, éliminant ainsi le besoin de Tuple.Create(...). De plus, pour utiliser les nouvelles fonctionnalités de tuple et la déstructuration, vous devez introduire le package NuGet System.ValueTuple.

Install-Package System.ValueTuple

Bien sûr, les tuples sont souvent utilisés pour les méthodes qui renvoient plusieurs valeurs. Certaines personnes aiment aussi utiliser les paramètres out pour les retours, mais même si les variables out sont désormais possibles, je ne suis toujours pas favorable à une utilisation généralisée des paramètres out.

L'exemple de méthode suivant est utilisé pour renvoyer une plage de temps par défaut (un total de 7 jours à compter d'aujourd'hui) pour la récupération des données.

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

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

Appelez cette méthode pour obtenir un tuple. Étant donné que la valeur de retour spécifie le nom de chaque membre de données lorsqu'elle est définie, l'obtention de données à partir d'un tuple peut être sémantique. Bien sûr, vous pouvez toujours utiliser <.> et Item1. Item2

var range = GetDefaultDateRange();
var begin = range.Begin;    // 也可以 begin = range.Item1
var end = range.End;        // 也可以 end = range.Item2
L'exemple ci-dessus peut être simplifié sans utiliser la variable intermédiaire

Cela utilise la déstructuration range

var (begin, end) = GetDefaultDateRange();
Le tuple créé ici est basé sur la valeur de retour. c'est une expression et peut créer des tuples n'importe où. La logique de l’exemple ci-dessus est très simple et peut être résolue à l’aide d’expressions. L'exemple suivant illustre une déclaration de type de retour non sémantique.

// 原来的 (DateTime Begin, DateTime End) 申明也是没问题的
(DateTime, DateTime) GetDefaultDateRange()
    => (DateTime.Today.AddDays(1).AddDays(-7), DateTime.Today.AddDays(1));
Méthode de déstructuration Deconstrct

La méthode de déstructuration permet à n'importe quelle classe (pas seulement les tuples) d'être déstructurée selon des paramètres définis. Et ce qui est étonnant, c'est que la méthode de déstructuration peut être une méthode membre ou définie comme une méthode d'extension.

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;
    }
}
Ce qui suit est le code utilisant la déstructuration

var size = new Size(1920, 1080, 10);
var (w, h) = size;
var (x, y, z) = size;
Transformer le constructeur de

Size

Rappelez-vous que le constructeur mentionné précédemment peut être défini comme un Lambda Style d'expression ? Voici une refonte du constructeur Size utilisant des tuples et Lambda - je suis déjà ivre !

public Size(int width, int height, int tall)
    => (Width, Height, Tall) = (width, height, tall);
Correspondance de modèles

La correspondance de modèles prend actuellement en charge

et is. Cela semble être un nom très noble, mais pour le dire de manière plus terre-à-terre, cela signifie juger du type et définir un type de référence spécifique. Si cela vous intéresse, vous pouvez ajouter quelques jugements supplémentaires. switch

Pour

, cela signifie définir une variable puis l'initialiser lors du jugement, donc le code écrit comme ceci is

// 假设逻辑能保证这里的 v 可能是 string 也 可能是 int
string ToString(object v) {
    if (v is int) {
        int n = (int) v;
        return n.ToString("X4");
    } else {
        return (string) n;
    }
}
peut être simplifié en - d'accord, directement Écrivez simplement en tant qu'expression en une seule étape

string ToString(object v)
    => (v is int n) ? n.ToString("X4") : (string) v;
Bien sûr, vous pouvez dire que la précédente peut également être simplifiée en une expression - d'accord, n'entrons pas dans ce problème, d'accord ? Je fais juste une démonstration de correspondance de modèles pour

. is

La correspondance de motifs dans

semble être beaucoup plus utile, prenons ToString comme exemple 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();
    }
}
Faites attention au

dans la première branche ci-dessus Juste du bon usage. when

Variable locale ref et valeur de retour ref

C'est déjà un usage très proche du C/C++. Bien que la déclaration officielle soit que cela peut résoudre certains problèmes de sécurité, personnellement, je n’ai pas encore rencontré de scénarios d’utilisation. Si le design est assez bon, avec les nouvelles fonctionnalités et la déconstruction des tuples actuellement ajoutées, je pense personnellement que l'utilisation de

et out peut presque être évitée. ref

Comme je ne l'ai pas utilisé, je n'en dirai pas plus, j'en discuterai avec les camarades de classe qui l'ont utilisé !

Amélioration de la syntaxe littérale numérique

Il y a deux améliorations ici L'une est l'introduction de la syntaxe littérale numérique binaire avec le préfixe

, et l'autre est que < peut être utilisé arbitrairement dans. 🎜> Numéros de groupe. Cela ne nécessite pas de majorité, donnez juste deux exemples pour comprendre 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";
    }
}

                                               


"[Traduction] Nouvelles fonctionnalités de C# 7" consacre beaucoup d'espace à la présentation des 9 nouvelles fonctionnalités de C# 7.0. Je vais ici les présenter rapidement à travers des exemples basés sur l'expérience du projet, afin que chacun puisse les apprendre. un court laps de temps. Apprenez-en davantage à leur sujet dans le temps.

En général, ces nouvelles fonctionnalités facilitent l'écriture de code avec des idées de programmation fonctionnelle en C# 7.0. C# 6.0 a fait beaucoup de travail sur cette voie, et C# 7.0 est un pas de plus !

Expression partout

En C# 6.0, vous pouvez utiliser des expressions Lambda pour les méthodes membres et les propriétés en lecture seule. La chose la plus frustrante à l'époque était la raison pour laquelle l'accesseur défini de la propriété n'était pas pris en charge. . Désormais, non seulement la méthode set prend en charge les expressions Lambda, mais les constructeurs, destructeurs et index peuvent également être définis dans les expressions Lambda.

class SomeModel
{
    private string internalValue;

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

out Variables

out Les variables sont une syntaxe qui existait auparavant. C# 7.0 lui permet simplement de regrouper la déclaration et l'utilisation pour éviter une ligne de code supplémentaire. L’effet le plus direct est que deux énoncés peuvent être complétés par une seule expression. Voici une version simplifiée de la classe Key à titre d'exemple. Cette classe a été utilisée par nous au début pour traiter les valeurs d'ID transmises via 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;
        }
    }
}

Mais c'est simple en C# 7

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

Tuple et déstructuration

Les amis qui ont utilisé System.Tuple doivent le connaîtreItem1, Item2 Je suis profondément offensé par une dénomination aussi dénuée de sens. Cependant, C# 7.0 apporte une dénomination sémantique et réduit en même temps la création de tuples, éliminant ainsi le besoin de Tuple.Create(...). De plus, pour utiliser les nouvelles fonctionnalités de tuple et la déstructuration, vous devez introduire le package NuGet System.ValueTuple.

Install-Package System.ValueTuple

Bien sûr, les tuples sont souvent utilisés pour les méthodes qui renvoient plusieurs valeurs. Certaines personnes aiment aussi utiliser les paramètres out pour les retours, mais même si les variables out sont désormais possibles, je ne suis toujours pas favorable à une utilisation généralisée des paramètres out.

L'exemple de méthode suivant est utilisé pour renvoyer une plage de temps par défaut (un total de 7 jours à compter d'aujourd'hui) pour la récupération des données.

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

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

Appelez cette méthode pour obtenir un tuple. Étant donné que la valeur de retour spécifie le nom de chaque membre de données lorsqu'elle est définie, l'obtention de données à partir d'un tuple peut être sémantique. Bien sûr, vous pouvez toujours utiliser <.> et Item1. Item2

var range = GetDefaultDateRange();
var begin = range.Begin;    // 也可以 begin = range.Item1
var end = range.End;        // 也可以 end = range.Item2
L'exemple ci-dessus peut être simplifié sans utiliser la variable intermédiaire

Cela utilise la déstructuration range

var (begin, end) = GetDefaultDateRange();
Le tuple créé ici est basé sur la valeur de retour. c'est une expression et peut créer des tuples n'importe où. La logique de l’exemple ci-dessus est très simple et peut être résolue à l’aide d’expressions. L'exemple suivant illustre une déclaration de type de retour non sémantique.

// 原来的 (DateTime Begin, DateTime End) 申明也是没问题的
(DateTime, DateTime) GetDefaultDateRange()
    => (DateTime.Today.AddDays(1).AddDays(-7), DateTime.Today.AddDays(1));
Méthode de déstructuration Deconstrct

La méthode de déstructuration permet à n'importe quelle classe (pas seulement les tuples) d'être déstructurée selon des paramètres définis. Et ce qui est étonnant, c'est que la méthode de déstructuration peut être une méthode membre ou définie comme une méthode d'extension.

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;
    }
}
Ce qui suit est le code utilisant la déstructuration

var size = new Size(1920, 1080, 10);
var (w, h) = size;
var (x, y, z) = size;
Transformer le constructeur de

Size

Rappelez-vous que le constructeur mentionné précédemment peut être défini comme un Lambda Style d'expression ? Voici une refonte du constructeur Size utilisant des tuples et Lambda - je suis déjà ivre !

public Size(int width, int height, int tall)
    => (Width, Height, Tall) = (width, height, tall);
Correspondance de modèles

La correspondance de modèles prend actuellement en charge

et is. Cela semble être un nom très noble, mais pour le dire de manière plus terre-à-terre, cela signifie juger du type et définir un type de référence spécifique. Si cela vous intéresse, vous pouvez ajouter quelques jugements supplémentaires. switch

Pour

, cela signifie définir une variable puis l'initialiser lors du jugement, donc le code écrit comme ceci is

// 假设逻辑能保证这里的 v 可能是 string 也 可能是 int
string ToString(object v) {
    if (v is int) {
        int n = (int) v;
        return n.ToString("X4");
    } else {
        return (string) n;
    }
}
peut être simplifié en - d'accord, directement Écrivez simplement en tant qu'expression en une seule étape

string ToString(object v)
    => (v is int n) ? n.ToString("X4") : (string) v;
Bien sûr, vous pouvez dire que la précédente peut également être simplifiée en une expression - d'accord, n'entrons pas dans ce problème, d'accord ? Je fais juste une démonstration de correspondance de modèles pour

. is

La correspondance de motifs dans

semble être beaucoup plus utile, prenons ToString comme exemple 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();
    }
}
Faites attention au

dans la première branche ci-dessus Juste du bon usage. when

Variable locale ref et valeur de retour ref

C'est déjà un usage très proche du C/C++. Bien que la déclaration officielle soit que cela peut résoudre certains problèmes de sécurité, personnellement, je n’ai pas encore rencontré de scénarios d’utilisation. Si le design est assez bon, avec les nouvelles fonctionnalités et la déconstruction des tuples actuellement ajoutées, je pense personnellement que l'utilisation de

et out peut presque être évitée. ref

Comme je ne l'ai pas utilisé, je n'en dirai pas plus, j'en discuterai avec les camarades de classe qui l'ont utilisé !

Amélioration de la syntaxe littérale numérique

Il y a deux améliorations ici L'une est l'introduction de la syntaxe littérale numérique binaire avec le préfixe

, et l'autre est que < peut être utilisé arbitrairement dans. 🎜> Numéros de groupe. Cela ne nécessite pas de majorité, donnez juste deux exemples pour comprendre 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";
    }
}



Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn