次の方法で共有


定数文字列でのパターン マッチ Span<char>

メモ

この記事は機能仕様についてです。 仕様は、機能の設計ドキュメントとして使用できます。 これには、提案された仕様の変更および機能の設計と開発時に必要な情報が含まれます。 これらの記事は、提案された仕様の変更が決定され、現在の ECMA 仕様に組み込まれるまで公開されます。

機能の仕様と行われた実装では、いくつかの違いがあることがあります。 これらの違いは、関連する言語設計ミーティング (LDM) メモに取り上げられています。

機能仕様を C# 言語標準に導入するプロセスの詳細については、仕様に関する記事を参照してください。

チャンピオンの課題: https://github.com/dotnet/csharplang/issues/8640

まとめ

Span<char> 型と ReadOnlySpan<char> 型の値を定数文字列に対してパターン マッチングできるようになります。

目的

パフォーマンスの観点から、多くのシナリオでは string 型よりも Span<char> 型と ReadOnlySpan<char> 型の使用が推奨されます。 このフレームワークでは、string の代わりに ReadOnlySpan<char> を使用できるように、多くの新しい API が追加されています。

文字列に対する一般的な操作は、特定の値であるかどうかを調べるために switch 文を使用することですが、コンパイラはこのような switch 文を最適化します。 しかし、ReadOnlySpan<char> に対して同様のことを効率的に行う方法は、switch 文を実装して最適化を手動で行う以外には、現在のところありません。

ReadOnlySpan<char> の導入を促進するために、ある ReadOnlySpan<char>を一定の stringとパターンマッチングできるようにし、それによってスイッチでの使用も可能にします。

static bool Is123(ReadOnlySpan<char> s)
{
    return s is "123";
}

static bool IsABC(Span<char> s)
{
    return s switch { "ABC" => true, _ => false };
}

詳細な設計

定数パターンの仕様を次のように変更します (追加部分は太字で示しています)。

パターン入力値 e および変換された値 v を持つ定数パターン P が与えられた場合、

  • e に整数型または列挙型、またはいずれかの null 許容形式があり、v に整数型がある場合、パターン P は、式 e == v の結果が true の場合、値 e一致します。それ以外の場合は次のようになります。
  • eSystem.Span<char> 型または System.ReadOnlySpan<char> 型であり、c が定数文字列であり、c の定数値が null ではない場合、System.MemoryExtensions.SequenceEqual<char>(e, System.MemoryExtensions.AsSpan(c))true を返すと、パターンが一致したとみなされます。
  • object.Equals(e, v)true を返す場合、パターン P は値 e一致します。

既知のメンバー

System.Span<T>System.ReadOnlySpan<T> は名前でマッチングされ、ref struct である必要があり、corlib の外部で定義できます。

System.MemoryExtensions は名前でマッチングされ、corlib の外部で定義できます。

System.MemoryExtensions.SequenceEqual オーバーロードの署名が一致している必要があります。

  • public static bool SequenceEqual<T>(System.Span<T>, System.ReadOnlySpan<T>)
  • public static bool SequenceEqual<T>(System.ReadOnlySpan<T>, System.ReadOnlySpan<T>)

System.MemoryExtensions.AsSpan のシグネチャは以下と一致する必要があります。

  • public static System.ReadOnlySpan<char> AsSpan(string)

パラメーターが省略可能なメソッドは対象外です。

デメリット

なし

代替

なし

未解決の質問

  1. マッチングは MemoryExtensions.SequenceEqual() などから独立して定義する必要がありますか?

    ... e のすべての文字について、e.Length == c.Length および e[i] == c[i] が成り立つ場合、パターンは一致するとみなされます。

    推奨: MemoryExtensions.SequenceEqual() を指標にして性能を定義します。 MemoryExtensions が見つからない場合は、コンパイル エラーを報告します。

  2. (string)null に対するマッチングは許可する必要がありますか?

    その場合、(string)null 以降 ""MemoryExtensions.AsSpan(null) == MemoryExtensions.AsSpan("") を含める必要がありますか?

    static bool IsEmpty(ReadOnlySpan<char> span)
    {
        return span switch
        {
            (string)null => true, // ok?
            "" => true,           // error: unreachable?
            _ => false,
        };
    }
    

    推奨: 定数パターン (string)null はエラーとして報告する必要があります。

  3. 定数パターン マッチングに Span<char> または ReadOnlySpan<char> の式値の実行時型検査は必要ですか?

    static bool Is123<T>(Span<T> s)
    {
        return s is "123"; // test for Span<char>?
    }
    
    static bool IsABC<T>(Span<T> s)
    {
        return s is Span<char> and "ABC"; // ok?
    }
    
    static bool IsEmptyString<T>(T t) where T : ref struct
    {
        return t is ""; // test for ReadOnlySpan<char>, Span<char>, string?
    }
    

    推奨: 定数パターンの実行時型検査は暗黙的に行われません。 (IsABC<T>() の例は、型検査が明示的であるため許可されます)

    この推奨は実装されませんでした。 前述のすべてのサンプルはコンパイル エラーとなります。

  4. サブサンプションで、定数文字列パターン、リストパターン、および Length プロパティパターンを考慮すべきですか?

    static int ToNum(ReadOnlySpan<char> s)
    {
        return s switch
        {
            { Length: 0 } => 0,
            "" => 1,        // error: unreachable?
            ['A',..] => 2,
            "ABC" => 3,     // error: unreachable?
            _ => 4,
        };
    }
    

    推奨事項: 式の値が stringの場合に使用されるのと同じサブサンプション動作。 これは、定数文字列、リストパターン、Lengthの間での包含がなく、[..] を何にでも一致するものとして扱うことを意味しますか?

デザイン会議

https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-10-07.md#readonlyspanchar-patterns