次の方法で共有


部分メソッドの拡張

メモ

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

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

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

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

まとめ

この提案は、C# の partial メソッドのシグネチャに関するすべての制限を削除することを目的としています。 目標は、これらのメソッドがソース ジェネレーターを操作できる一連のシナリオを拡張すること、および C# メソッドのより一般的な宣言フォームにすることです。

元の部分メソッドの仕様 (§15.6.9) も参照してください。

目的

C# では、開発者がメソッドを宣言と定義/実装に分割するためのサポートが制限されています。

partial class C
{
    // The declaration of C.M
    partial void M(string message);
}

partial class C
{
    // The definition of C.M
    partial void M(string message) => Console.WriteLine(message);
}

partial メソッドの動作の 1 つとして、定義が存在しない場合、言語は partial メソッドの呼び出しを単純に消去することが挙げられます。 基本的には、条件が false に評価された [Conditional] メソッドの呼び出しのように動作します。

partial class D
{
    partial void M(string message);

    void Example()
    {
        M(GetIt()); // Call to M and GetIt erased at compile time
    }

    string GetIt() => "Hello World";
}

この機能の本来の動機は、設計者によって生成されたコードの形式でのソース生成でした。 生成されたコードの一部をフックする必要があるため、ユーザーは生成されたコードを常に編集していました。 特に、コンポーネントが初期化された後の Windows フォームのスタートアップ プロセスの一部です。

生成されたコードの編集は、設計者がコードを再生成する原因となったアクションによってユーザーの編集が消去されるため、エラーが発生しやすくなります。 partial メソッド機能は、設計者が partial メソッドの形式でフックを出力できるため、この緊張を緩和しました。

デザイナーは partial void OnComponentInit() のようなフックを出力することができます。開発者はそれらのフックの宣言を定義するかどうかを選択できます。 どちらの場合も、生成されたコードがコンパイルされ、プロセスに関心がある開発者は必要に応じてフック インできます。

これは、部分メソッドにはいくつかの制限があることを意味します。

  1. void 戻り値の型を持たなければなりません。
  2. out パラメータを含むことはできません。
  3. アクセシビリティを持つことはできません (暗黙的に private)。

これらの制限は、呼び出し側が消去されたときに言語がコードを出力できる必要があるためです。 メンバーはアセンブリ メタデータで公開できないため、消去できる場合は private が唯一のアクセシビリティです。 これらの制限は、partial メソッドを適用できる一連のシナリオを制限するためにも役立ちます。

ここでの提案では、partial メソッドに関する既存の制限をすべて削除します。 基本的に、out パラメーター、void 以外の戻り値の型、または任意の種類のアクセシビリティを持たせます。 このような partial 宣言では、定義が存在する必要があることを追加する必要があります。 つまり、言語は通話側の消去の影響を考慮する必要はありません。

これにより、partial メソッドが参加できるジェネレーター シナリオのセットが拡張され、ソース ジェネレーター機能とうまくリンクされます。 たとえば、regex は、次のパターンを使用して定義できます。

[RegexGenerated("(dog|cat|fish)")]
partial bool IsPetMatch(string input);

これにより、ジェネレーターを選択する簡単な宣言型の方法を開発者に与えるだけでなく、ジェネレーターに、生成された出力を駆動するためのソースコードを確認するための非常に簡単な宣言一式を与えます。

これを、ジェネレーターが次のコード スニペットをフックする難しさと比較します。

var regex = new RegularExpression("(dog|cat|fish)");
if (regex.IsMatch(someInput))
{

}

コンパイラがジェネレーターにコード フックを変更することを許可していないので、このパターンをフックすることは、ジェネレーターにとってほとんど不可能です。 IsMatch 実装でのリフレクションに頼るか、呼び出し側を新しいメソッドに変更し、正規表現をリファクタリングして文字列リテラルを引数として渡すようにユーザーに依頼する必要があります。 それはかなり乱雑です。

詳細な設計

言語が変更され、partial メソッドに明示的なアクセシビリティ修飾子で注釈を付けることができます。 つまり、privatepublic などのラベルを付けることができます。

partial メソッドに明示的なアクセシビリティ修飾子がある場合、言語では、アクセシビリティが private である場合でも、宣言に一致する定義が必要になります。

partial class C
{
    // Okay because no definition is required here
    partial void M1();

    // Okay because M2 has a definition
    private partial void M2();

    // Error: partial method M3 must have a definition
    private partial void M3();
}

partial class C
{
    private partial void M2() { }
}

さらに、明示的なアクセシビリティを持つ partial メソッドに表示される可能性のあるすべての制限が言語によって削除されます。 このような宣言には、void 以外の戻り値の型、out パラメーター、extern 修飾子などを含めることができます。これらのシグネチャは、C# 言語の完全な表現力を持つことになります。

partial class D
{
    // Okay
    internal partial bool TryParse(string s, out int i); 
}

partial class D
{
    internal partial bool TryParse(string s, out int i) { ... }
}

このことにより、partial メソッドが overrides および interface の実装に明示的に含まれることができます。

interface IStudent
{
    string GetName();
}

partial class C : IStudent
{
    public virtual partial string GetName(); 
}

partial class C
{
    public virtual partial string GetName() => "Jarde";
}

コンパイラは、partial メソッドに無効な要素が含まれている場合に生成されるエラーを、本質的に次のように変更します。

明示的なアクセシビリティがない partial メソッドで ref を使用できない

これは、この機能を使用する際に開発者を正しい方向に向けるのに役立ちます。

制限:

  • 明示的なアクセシビリティを持つ partial 宣言には、定義が必要です
  • partial 宣言と定義シグネチャは、すべてのメソッドおよびパラメーター修飾子で一致する必要があります。 異なる可能性がある唯一の側面は、パラメーター名と属性リストです (これは新しいものではなく、partial メソッドの既存の要件です)。

質問

すべてのメンバーの一部に限定

ソース ジェネレーターに対してよりわかりやすい partial を拡張していることを考えると、すべてのクラス メンバーで動作するように拡張する必要がありますか? たとえば、partial コンストラクターや演算子などを宣言できる必要があります。

解決策 アイデアは健全ですが、C# 9 のスケジュールのこの時点で、不要なフィーチャー クリープを回避しようとしています。 最新のソース ジェネレーターを使用するように機能を拡張する場合に、即時の問題を解決したいと考えています。

他のメンバーをサポートするために partial を拡張することは、C# 10 リリースで検討されます。 この拡張機能を検討する可能性が高いと思われます。 これはアクティブな提案のままですが、まだ実装されていません。

部分ではなく抽象を使用する

この提案の核心は、基本的に宣言が対応する定義/実装を持っていることを保証しています。 すでに開発者に実装について考えさせる言語キーワードであるため、 abstract を使用する必要があるでしょうか?

解決 これについては健全な議論がありましたが、最終的には反対になりました。 はい、要件は把握していますが、概念は大きく異なります。 スロットを作成していなくても、開発者が仮想スロットを作成していると信じるように簡単に誘導できました。