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

巴扎黑
リリース: 2017-04-15 09:05:55
オリジナル
2048 人が閲覧しました

「[翻訳] 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 サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート