目次
どこでも式
元组和解构
解构方法 Deconstrct
改造 Size 的构造方法
模式匹配
ref 局部变量和 ref 返回值
数字字面量语法增强
構造化メソッド Deconstrct
Size
局部函数
支持更多 async 返回类型
ホームページ バックエンド開発 C#.Net チュートリアル C# 7.0 の新機能 (クイック プレビュー)

C# 7.0 の新機能 (クイック プレビュー)

Apr 15, 2017 am 09:05 AM

「[翻訳] 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 变量

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);
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

调用这个方法可以获得元组,因为定义的时候返回值指定了每个数据成员的名称,所以从元组获取数据可以是语义化的,当然仍然可以使用 Item1Item2

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 表达式吗?下面是使用元组和 Lambda 对 Size 构造方法的改造——我已经醉了!

public Size(int width, int height, int tall)
    => (Width, Height, Tall) = (width, height, tall);
ログイン後にコピー
ログイン後にコピー

模式匹配

模式匹配目前支持 isswitch。说起来挺高大上的一个名字,换个接地气一点的说法就是判断类型顺便定义个具体类型的引用,有兴趣还可以加再点额外的判断。

对于 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 的模式匹配而已。

switch 中的模式匹配似乎要有用得多,还是以 ToString 为例吧

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++ 的一种用法了。虽然官方说法是这样做可以解决一些安全性问题,但我个人目前还是没遇到它的使用场景。如果设计足够好,在目前又加入了元组新特性和解构的情况下,个人认为几乎可以避免使用 outref

既然没用到,我也不多说了,有用到的同学来讨论一下!

数字字面量语法增强

这里有两点增强,一点是引入了 0b 前缀的二进制数字面量语法,另一点是可以在数值字面量中任意使用 _

out 変数は、以前から存在していた構文であり、コードを 1 行増やす必要がないように宣言して一緒に使用することができます。最も直接的な効果は、2 つのステートメントを 1 つの式で完了できることです。ここでは、例として Key クラスの簡略化したバージョンを示します。このクラスは、HTTP Get/Post を通じて渡された ID 値を処理するために初期に使用されました。 🎜
const int MARK_THREE = 0b11;            // 0x03
const int LONG_MARK = 0b_1111_1111;     // 0xff
const double PI = 3.14_1592_6536
ログイン後にコピー
ログイン後にコピー
🎜しかし、C# 7 では簡単です🎜
Install-Package System.Threading.Tasks.Extensions
ログイン後にコピー
ログイン後にコピー
🎜タプルと構造解除🎜🎜 System.Tuple を使用したことがある友人は、その Item1Item2 < についてよく知っているはずです。 /code> このような意味のない名前に非常に腹を立てます。ただし、C# 7.0 ではセマンティックな名前付けが可能になり、同時にタプルの作成が減り、<code>Tuple.Create(...) の必要性がなくなりました。さらに、新しいタプル機能と構造化を使用するには、NuGet パッケージ System.ValueTuple をインポートする必要があります。 🎜
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";
    }
}
ログイン後にコピー
ログイン後にコピー
🎜もちろん、タプルは複数の値を返すメソッドによく使用されます。 out パラメータを使用して返すことを好む人もいますが、out 変数が使用できるようになったとしても、私は out の広範な使用をまだサポートしていません。コード>パラメータ。 🎜🎜次のメソッド例は、データ取得のデフォルトの時間範囲 (今日から合計 7 日間) を返すために使用されます。 🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">class SomeModel {     private string internalValue;     public string Value     {         get =&gt; internalValue;         set =&gt; internalValue = string.IsNullOrWhiteSpace(value) ? null : value;     } }</pre><div class="contentsignin">ログイン後にコピー</div></div><div class="contentsignin">ログイン後にコピー</div></div><div class="contentsignin">ログイン後にコピー</div></div><div class="contentsignin">ログイン後にコピー</div></div>🎜このメソッドを呼び出してタプルを取得します。定義時に戻り値で各データ メンバーの名前が指定されるため、タプルからのデータの取得は引き続き <code>Item1を使用できます。 > と Item2。 🎜
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;
        }
    }
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
🎜上記の例は、中間変数 range を使用せずに簡略化できます。これは、構造化を使用します。🎜
// 注意 out var intValue,
// 对于可推导的类型甚至可以用 var 来申明变量
public int IntValue => int.TryParse(Value, out var intValue) ? intValue : 0;
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
🎜実際には、タプルは戻り値に基づいています。どこでも作成できます。上記の例のロジックは非常に単純で、式を使用して解決できます。次の例は、非セマンティックな戻り値の型宣言を示しています。 🎜
Install-Package System.ValueTuple
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

構造化メソッド Deconstrct

🎜 構造化メソッドを使用すると、定義されたパラメーターに従って (タプルだけでなく) 任意のクラスを構造化できます。そして驚くべきことに、分割メソッドはメンバー メソッドにすることも、拡張メソッドとして定義することもできるということです。 🎜
// 返回类型是一个包含两个元素的元组
(DateTime Begin, DateTime End) GetDefaultDateRange()
{
    var end = DateTime.Today.AddDays(1);
    var begin = end.AddDays(-7);

    // 这里使用一对圆括号就创建了一个元组
    return (begin, end);
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
🎜以下は、構造解除を使用したコードです🎜​​
var range = GetDefaultDateRange();
var begin = range.Begin;    // 也可以 begin = range.Item1
var end = range.End;        // 也可以 end = range.Item2
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

Size

のコンストラクターを変換します🎜前述のコンストラクターはラムダ式として定義できることを覚えていますか?これは、タプルと Lambda を使用した Size コンストラクターの改良です - もう酔っています。 🎜
var (begin, end) = GetDefaultDateRange();
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
🎜パターン マッチング🎜🎜 パターン マッチングは現在、isswitch をサポートしています。非常に高尚な名前のように聞こえますが、より現実的な方法で言うと、タイプを判断し、特定のタイプの参照を定義することを意味します。興味がある場合は、追加の判断を追加できます。 🎜🎜 is については、変数を定義して判定時に初期化することを意味するので、このように記述されたコード 🎜
// 原来的 (DateTime Begin, DateTime End) 申明也是没问题的
(DateTime, DateTime) GetDefaultDateRange()
    => (DateTime.Today.AddDays(1).AddDays(-7), DateTime.Today.AddDays(1));
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
🎜 は次のように簡略化できます - まあ、ワンステップで式に書き込むだけでOKです🎜
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;
    }
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
🎜 もちろん、前の式も式に簡略化できると言われるかもしれません。わかりました、この問題については深く掘り下げないでください。ここでは、is のパターン マッチングを示しているだけです。 🎜
🎜 switch でのパターン マッチングの方がはるかに便利なようです。ToString を例にしてみましょう🎜
var size = new Size(1920, 1080, 10);
var (w, h) = size;
var (x, y, z) = size;
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
🎜 上記の最初のブランチの when に注意してください使用感はちょうどいいです。 🎜🎜ref ローカル変数と ref 戻り値🎜🎜 これはすでに C/C++ に非常に近い使用法です。公式発表では、これによりいくつかのセキュリティ問題が解決できるとされていますが、私個人としては、これまでのところその使用シナリオに遭遇したことがありません。設計が十分に優れていれば、現在追加されている新機能とタプルの分解により、outref の使用はほぼ回避できると個人的には思います。 🎜🎜私は使ったことがないので、これ以上は使用したことのあるクラスメートと話してみます。 🎜🎜数値リテラル構文の拡張🎜🎜 ここには 2 つの拡張があります。1 つは、0b プレフィックスを伴うバイナリ デジタル リテラル構文の導入であり、もう 1 つは、_ を数値リテラルで任意に使用できることです。 . グループ番号。これには過半数は必要ありませんが、理解するために 2 つの例を挙げてください🎜
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> 就比较像 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 变量

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);
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

调用这个方法可以获得元组,因为定义的时候返回值指定了每个数据成员的名称,所以从元组获取数据可以是语义化的,当然仍然可以使用 Item1Item2

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 表达式吗?下面是使用元组和 Lambda 对 Size 构造方法的改造——我已经醉了!

public Size(int width, int height, int tall)
    => (Width, Height, Tall) = (width, height, tall);
ログイン後にコピー
ログイン後にコピー

模式匹配

模式匹配目前支持 isswitch。说起来挺高大上的一个名字,换个接地气一点的说法就是判断类型顺便定义个具体类型的引用,有兴趣还可以加再点额外的判断。

对于 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 的模式匹配而已。

switch 中的模式匹配似乎要有用得多,还是以 ToString 为例吧

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++ 的一种用法了。虽然官方说法是这样做可以解决一些安全性问题,但我个人目前还是没遇到它的使用场景。如果设计足够好,在目前又加入了元组新特性和解构的情况下,个人认为几乎可以避免使用 outref

既然没用到,我也不多说了,有用到的同学来讨论一下!

数字字面量语法增强

这里有两点增强,一点是引入了 0b 前缀的二进制数字面量语法,另一点是可以在数值字面量中任意使用 _

out 変数は、以前から存在していた構文であり、コードを 1 行増やす必要がないように宣言して一緒に使用することができます。最も直接的な効果は、2 つのステートメントを 1 つの式で完了できることです。ここでは、例として Key クラスの簡略化したバージョンを示します。このクラスは、HTTP Get/Post を通じて渡された ID 値を処理するために初期に使用されました。 🎜
const int MARK_THREE = 0b11;            // 0x03
const int LONG_MARK = 0b_1111_1111;     // 0xff
const double PI = 3.14_1592_6536
ログイン後にコピー
ログイン後にコピー
🎜しかし、C# 7 では簡単です🎜
Install-Package System.Threading.Tasks.Extensions
ログイン後にコピー
ログイン後にコピー
🎜タプルと構造解除🎜🎜 System.Tuple を使用したことがある友人は、その Item1Item2 < についてよく知っているはずです。 /code> このような意味のない名前に非常に腹を立てます。ただし、C# 7.0 ではセマンティックな名前付けが可能になり、同時にタプルの作成が減り、<code>Tuple.Create(...) の必要性がなくなりました。さらに、新しいタプル機能と構造化を使用するには、NuGet パッケージ System.ValueTuple をインポートする必要があります。 🎜
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";
    }
}
ログイン後にコピー
ログイン後にコピー
🎜もちろん、タプルは複数の値を返すメソッドによく使用されます。 out パラメータを使用して返すことを好む人もいますが、out 変数が使用できるようになったとしても、私は out の広範な使用をまだサポートしていません。コード>パラメータ。 🎜🎜次のメソッド例は、データ取得のデフォルトの時間範囲 (今日から合計 7 日間) を返すために使用されます。 🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">class SomeModel {     private string internalValue;     public string Value     {         get =&gt; internalValue;         set =&gt; internalValue = string.IsNullOrWhiteSpace(value) ? null : value;     } }</pre><div class="contentsignin">ログイン後にコピー</div></div><div class="contentsignin">ログイン後にコピー</div></div><div class="contentsignin">ログイン後にコピー</div></div><div class="contentsignin">ログイン後にコピー</div></div>🎜このメソッドを呼び出してタプルを取得します。定義時に戻り値で各データ メンバーの名前が指定されるため、タプルからのデータの取得は引き続き <code>Item1を使用できます。 > と Item2。 🎜
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;
        }
    }
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
🎜上記の例は、中間変数 range を使用せずに簡略化できます。これは、構造化を使用します。🎜
// 注意 out var intValue,
// 对于可推导的类型甚至可以用 var 来申明变量
public int IntValue => int.TryParse(Value, out var intValue) ? intValue : 0;
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
🎜実際には、タプルは戻り値に基づいています。どこでも作成できます。上記の例のロジックは非常に単純で、式を使用して解決できます。次の例は、非セマンティックな戻り値の型宣言を示しています。 🎜
Install-Package System.ValueTuple
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

構造化メソッド Deconstrct

🎜 構造化メソッドを使用すると、定義されたパラメーターに従って (タプルだけでなく) 任意のクラスを構造化できます。そして驚くべきことに、分割メソッドはメンバー メソッドにすることも、拡張メソッドとして定義することもできるということです。 🎜
// 返回类型是一个包含两个元素的元组
(DateTime Begin, DateTime End) GetDefaultDateRange()
{
    var end = DateTime.Today.AddDays(1);
    var begin = end.AddDays(-7);

    // 这里使用一对圆括号就创建了一个元组
    return (begin, end);
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
🎜以下は、構造解除を使用したコードです🎜​​
var range = GetDefaultDateRange();
var begin = range.Begin;    // 也可以 begin = range.Item1
var end = range.End;        // 也可以 end = range.Item2
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

Size

のコンストラクターを変換します🎜前述のコンストラクターはラムダ式として定義できることを覚えていますか?これは、タプルと Lambda を使用した Size コンストラクターの改良です - もう酔っています。 🎜
var (begin, end) = GetDefaultDateRange();
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
🎜パターン マッチング🎜🎜 パターン マッチングは現在、isswitch をサポートしています。非常に高尚な名前のように聞こえますが、より現実的な方法で言うと、タイプを判断し、特定のタイプの参照を定義することを意味します。興味がある場合は、追加の判断を追加できます。 🎜🎜 is については、変数を定義して判定時に初期化することを意味するので、このように記述されたコード 🎜
// 原来的 (DateTime Begin, DateTime End) 申明也是没问题的
(DateTime, DateTime) GetDefaultDateRange()
    => (DateTime.Today.AddDays(1).AddDays(-7), DateTime.Today.AddDays(1));
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
🎜 は次のように簡略化できます - まあ、ワンステップで式に書き込むだけでOKです🎜
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;
    }
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
🎜 もちろん、前の式も式に簡略化できると言われるかもしれません。わかりました、この問題については深く掘り下げないでください。ここでは、is のパターン マッチングを示しているだけです。 🎜
🎜 switch でのパターン マッチングの方がはるかに便利なようです。ToString を例にしてみましょう🎜
var size = new Size(1920, 1080, 10);
var (w, h) = size;
var (x, y, z) = size;
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
🎜 上記の最初のブランチの when に注意してください使用感はちょうどいいです。 🎜🎜ref ローカル変数と ref 戻り値🎜🎜 これはすでに C/C++ に非常に近い使用法です。公式発表では、これによりいくつかのセキュリティ問題が解決できるとされていますが、私個人としては、これまでのところその使用シナリオに遭遇したことがありません。設計が十分に優れていれば、現在追加されている新機能とタプルの分解により、outref の使用はほぼ回避できると個人的には思います。 🎜🎜私は使ったことがないので、これ以上は使用したことのあるクラスメートと話してみます。 🎜🎜数値リテラル構文の拡張🎜🎜 ここには 2 つの拡張があります。1 つは、0b プレフィックスを伴うバイナリ デジタル リテラル構文の導入であり、もう 1 つは、_ を数値リテラルで任意に使用できることです。 . グループ番号。これには過半数は必要ありませんが、理解するために 2 つの例を挙げてください🎜
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> 就比较像 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 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

PHP 8.3 リリース: 新機能の概要 PHP 8.3 リリース: 新機能の概要 Nov 27, 2023 pm 12:52 PM

PHP8.3 リリース: 新機能の概要 テクノロジーが発展し、ニーズが変化するにつれて、プログラミング言語は常に更新され、改善されています。 Web 開発で広く使用されているスクリプト言語として、PHP は開発者により強力で効率的なツールを提供するために絶えず改良されてきました。最近リリースされた PHP 8.3 バージョンには、待望の新機能と改善点が多数含まれています。これらの新機能の概要を見てみましょう。 null 以外のプロパティの初期化 PHP の過去のバージョンでは、クラス プロパティに明示的に値が割り当てられていない場合、その値は

PHP8 の新機能を学び、最新テクノロジーを深く理解するためのガイド PHP8 の新機能を学び、最新テクノロジーを深く理解するためのガイド Dec 23, 2023 pm 01:16 PM

最新テクノロジーを習得するために役立つ、PHP8 の新機能の詳細な分析 PHP プログラミング言語は、時間の経過とともに常に進化し、改善されています。最近リリースされた PHP8 バージョンは、開発者に多くのエキサイティングな新機能と改善を提供し、開発作業にさらなる利便性と効率をもたらします。この記事では、PHP8 の新機能を詳しく分析し、これらの最新テクノロジーをよりよく習得するのに役立つ具体的なコード例を提供します。 JIT コンパイラー PHP8 では JIT (Just-In-Time) コンパイルが導入されています

PHP8.1 で導入された新しい Redis 拡張機能 PHP8.1 で導入された新しい Redis 拡張機能 Jul 07, 2023 pm 09:41 PM

PHP8.1 で導入された新しい Redis 拡張機能 インターネットの急速な発展に伴い、大量のデータを保存および処理する必要があります。データ処理の効率とパフォーマンスを向上させるために、キャッシュは不可欠な部分になっています。 PHP 開発では、Redis は高性能のキー/値ストレージ システムとして、キャッシュやデータ ストレージのシナリオで広く使用されています。 PHP で Redis を使用するエクスペリエンスをさらに向上させるために、PHP8.1 では新しい Redis 拡張機能が導入されています。この記事では、この拡張機能の新機能と提供内容を紹介します。

CSS3 の新機能の概要: CSS3 を使用してトランジション効果を実現する方法 CSS3 の新機能の概要: CSS3 を使用してトランジション効果を実現する方法 Sep 09, 2023 am 11:27 AM

CSS3 の新機能の概要: CSS3 を使用してトランジション効果を実現する方法 CSS3 は CSS の最新バージョンですが、数多くの新機能の中で最も興味深く実用的なのはトランジション効果でしょう。トランジション効果は、インタラクション中にページをよりスムーズで美しくし、ユーザーに優れた視覚体験を提供します。この記事では、CSS3 トランジション効果の基本的な使用法を、対応するコード例とともに紹介します。 transition-property 属性: 遷移する必要がある CSS プロパティの遷移効果を指定します。

Go 言語の新機能の解釈: プログラミングをより効率的にする Go 言語の新機能の解釈: プログラミングをより効率的にする Mar 10, 2024 pm 12:27 PM

【Go言語の新機能解説:プログラミングを効率化するには具体的なコード例が必要】 近年、ソフトウェア開発の分野でGo言語が注目を集めており、そのシンプルで効率的な設計思想がますます注目を集めています。開発者。 Go 言語は静的型付けプログラミング言語として、開発効率を向上させ、コード作成プロセスを簡素化するための新機能を導入し続けています。この記事では、Go 言語の最新機能を詳しく説明し、これらの新機能によってもたらされる利便性を具体的なコード例を通じて体験する方法について説明します。モジュール開発(GoModules) 1からのGo言語

php8の新機能は何ですか php8の新機能は何ですか Sep 25, 2023 pm 01:34 PM

php8 の新機能には、JIT コンパイラ、型推論、名前付きパラメータ、共用体型、プロパティ、エラー処理の改善、非同期プログラミングのサポート、新しい標準ライブラリ関数、匿名クラス拡張機能が含まれます。詳細な紹介: 1. JIT コンパイラー、PHP8 は、重要なパフォーマンスの向上である JIT コンパイラーを導入します。JIT コンパイラーは、一部の高頻度の実行コードをリアルタイムでコンパイルおよび最適化できるため、実行速度が向上します。2. 型導出、PHP8型推論機能を導入し、開発者が変数宣言時などに変数の型を自動的に推測できるようにします。

CSS3 の新機能の概要: CSS3 を使用して水平方向の中央揃えのレイアウトを実現する方法 CSS3 の新機能の概要: CSS3 を使用して水平方向の中央揃えのレイアウトを実現する方法 Sep 09, 2023 pm 04:09 PM

CSS3 の新機能の概要: CSS3 を使用して水平方向中央レイアウトを実現する方法 Web デザインとレイアウトでは、水平方向中央レイアウトが一般的な要件です。これまでは、これを実現するために複雑な JavaScript や CSS のトリックを使用することがよくありました。ただし、CSS3 では、水平方向中央揃えのレイアウトをよりシンプルかつ柔軟にするいくつかの新機能が導入されました。この記事では、CSS3 のいくつかの新機能を紹介し、CSS3 を使用して水平方向の中央揃えのレイアウトを実現する方法を示すコード例をいくつか示します。 1. フレックスボックスを使用してファイルをレイアウトする

PHP8 の基礎となる開発原則の解読: 新機能を使用してコードのパフォーマンスと信頼性を向上させる PHP8 の基礎となる開発原則の解読: 新機能を使用してコードのパフォーマンスと信頼性を向上させる Sep 10, 2023 pm 04:25 PM

PHP プログラミング言語の最新バージョンである PHP8 には、多くの新機能と改善が導入されており、基礎となる開発原則にいくつかの重要な変更が加えられています。この記事では、PHP8 の基礎となる開発原則を詳しく掘り下げ、新機能を使用してコードのパフォーマンスと信頼性を向上させる方法を分析します。まず、PHP8 の基礎となる重要な開発原則をいくつか見てみましょう。 PHP8 では、JIT (Just-In-Time) コンパイラーが導入されています。これは、PHP コードをオンザフライでローカル マシン コードに変換し、実行中にコンパイルできる動的コンパイラーです。

See all articles