Partager via


Propriétés partielles

Remarque

Cet article est une spécification de fonctionnalité. La spécification sert de document de conception pour la fonctionnalité. Il inclut les modifications de spécification proposées, ainsi que les informations nécessaires pendant la conception et le développement de la fonctionnalité. Ces articles sont publiés jusqu’à ce que les modifications de spécification proposées soient finalisées et incorporées dans la spécification ECMA actuelle.

Il peut y avoir des différences entre la spécification de la fonctionnalité et l’implémentation terminée. Ces différences sont consignées dans les notes pertinentes de la réunion de conception linguistique (LDM).

Vous pouvez en savoir plus sur le processus d’adoption des speclets de fonctionnalités dans la norme de langage C# dans l’article sur les spécifications .

Problème de champion : https://github.com/dotnet/csharplang/issues/6420

Grammaire

La grammaire property_declaration (§14.7.1) est mise à jour comme suit :

property_declaration
-    : attributes? property_modifier* type member_name property_body
+    : attributes? property_modifier* 'partial'? type member_name property_body
    ;  

Remarques: il s’agit d’un peu similaire à la façon dont method_header(§15.6.1) et class_declaration(§15.2.1) sont spécifiées. (Notez que le Problème n° 946 propose de assouplir l’exigence de classement et s’appliquerait probablement à toutes les déclarations qui autorisent le modificateur partial. Notre but es de spécifier cet assouplissement du classement dans un futur proche, et de l’implémenter dans la même version que cette fonctionnalité.)

Définition et implémentation de déclarations

Lorsqu’une déclaration de propriété inclut un modificateur partiel, cette propriété est considérée comme une propriété partielle. Les propriétés partielles peuvent uniquement être déclarées en tant que membres de types partiels.

Une déclaration de propriété partielle est considérée comme une déclaration définissant lorsque ses accesseurs ont tous des corps point-virgules et qu’il n’a pas le modificateur extern. Sinon, c’est une déclaration d’implémentation.

partial class C
{
    // Defining declaration
    public partial string Prop { get; set; }

    // Implementing declaration
    public partial string Prop { get => field; set => field = value; }
}

Étant donné que nous avons réservé la forme syntaxique avec des corps d’accesseur avec point-virgule pour la déclaration définissante , une propriété partielle ne peut pas être automatiquement implémentée . Nous adaptons donc propriétés implémentées automatiquement (§15.7.4) comme suit :

Une propriété implémentée automatiquement (ou propriété automatique en abrégé) est une propriété non abstraite, non externe, non partielle,, non valorisée avec des corps d’accesseur à point-virgule uniquement.

Remarques. Il est utile pour le compilateur de pouvoir examiner une seule déclaration en isolation et savoir s’il s’agit d’une définition ou d’une déclaration d’implémentation. Par conséquent, nous ne voulons pas autoriser les propriétés automatiques en incluant deux déclarations de propriétés partial identiques, par exemple. Nous ne pensons pas que les cas d’usage de cette fonctionnalité impliquent l’implémentation de la propriété partielle avec une propriété automatique, mais dans les cas où une implémentation triviale est souhaitée, nous pensons que le mot clé field rend les choses assez simples.


Une propriété partielle doit avoir une définition de la déclaration et une implémentation de la déclaration.

Remarques. Nous ne pensons pas non plus qu’il est utile de fractionner la déclaration entre plus de deux parties, pour permettre à différents accesseurs d’être implémentés dans différents endroits, par exemple. Par conséquent, nous imiterons simplement le schéma établi par des méthodes partielles.


Seule la déclaration de définition d’une propriété partielle participe à la recherche, similaire à la façon dont seule la déclaration de définition d’une méthode partielle participe à la résolution de surcharge.

Remarques. Dans le compilateur, nous nous attendons à ce que seul le symbole de la déclaration de définition apparaisse dans la liste des membres et que le symbole de la partie d’implémentation soit accessible via le symbole de définition. Toutefois, certaines fonctionnalités telles que l’analyse nullable peuvent voir à travers la déclaration d’implémentation afin de fournir un comportement plus utile.

partial class C
{
    public partial string Prop { get; set; }
    public partial string Prop { get => field; set => field = value; }

    public C() // warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
    {
    }
}

Une propriété partielle n’est pas autorisée à avoir le modificateur abstract.

Une propriété partielle ne peut pas implémenter explicitement les propriétés d’interface.

Fusion d’attributs

Comme pour les méthodes partielles, les attributs de la propriété résultante sont les attributs combinés des parties sont concaténés dans un ordre non spécifié et les doublons ne sont pas supprimés.

Attributs d’informations de l’appelant

Nous adaptons le langage suivant à partir du standard :

C’est une erreur d’avoir le même attribut d’informations de l’appelant sur un paramètre de la partie définition et de la partie implémentation d’une déclaration de méthodemembre partielle. Seuls les attributs d’informations de l’appelant dans la partie de définition sont appliqués, tandis que les attributs d’informations de l’appelant qui se produisent uniquement dans le composant d’implémentation sont ignorés.

  • L’erreur décrite sort des définitions de ces attributs qui n’ont pas AllowMultiple = true. L’utilisation de ces éléments plusieurs fois, y compris entre les déclarations partielles, entraîne une erreur.
  • Lorsque les attributs d’informations de l’appelant sont appliqués à un paramètre dans la partie implémentation d’une méthode partielle, le compilateur Roslyn signale un avertissement. Il lance également un avertissement pour le même scénario dans une propriété partielle.

Signatures correspondantes

Lors de la réunion LDM du 14 septembre 2020 nous avons défini un ensemble d’exigences « strictes » pour la correspondance de signature des méthodes partielles, qui ont été introduites dans une vague d’avertissements. Les propriétés partielles ont des exigences analogues à celles des méthodes partielles pour correspondre aux signatures autant que possible, sauf que tous les diagnostics d'incompatibilité sont signalés par défaut et ne sont pas masqués par une vague d'avertissement.

Les exigences de correspondance des signatures sont les suivantes :

  1. Les différences de type et de type ref entre les déclarations de propriétés partielles qui sont significatives pour le runtime entraînent une erreur au moment de la compilation.
  2. Les différences entre les noms d’éléments tuple dans les déclarations de propriétés partielles entraînent une erreur au moment de la compilation, identique à celle des méthodes partielles.
  3. Les déclarations de propriété et leurs déclarations d’accesseur doivent avoir les mêmes modificateurs, bien que les modificateurs puissent apparaître dans un ordre différent.
    • Exception : cela ne s’applique pas au modificateur extern, qui peut apparaître uniquement sur une déclaration d’implémentation de .
  4. Toutes les autres différences syntactiques dans les signatures de déclarations de propriété partielles entraînent un avertissement au moment de la compilation, avec les exceptions suivantes :
    • Les listes d’attributs sur ou dans les déclarations de propriétés partielles n’ont pas besoin de correspondre. Au lieu de cela, la fusion des attributs dans les positions correspondantes est effectuée conformément à la Fusion d’attributs.
    • Les différences de contexte de types nullables ne génèrent pas d’avertissements. En d’autres termes, une différence dont l’un des types est nullable-oblivious et dont l’autre type est soit nullable-annotated, soit non-nullable-annotated, n’entraîne aucun avertissement.
    • Les valeurs de paramètre par défaut n’ont pas besoin de correspondre. Un avertissement est signalé lorsque la partie implémentation d’un indexeur partiel a des valeurs de paramètre par défaut. Il s’agit d’un avertissement existant qui se produit lorsque la partie implémentation d’une méthode partielle a des valeurs de paramètre par défaut.
  5. Un avertissement se produit lorsque les noms de paramètres diffèrent entre la définition et l’implémentation de déclarations. Les noms de paramètres de la partie définition sont utilisés sur les sites d’utilisation et dans l’émission.
  6. Les différences de possibilité de valeurs nulles qui n’impliquent pas une nullabilité non consciente génèrent des avertissements. Lors de l’analyse d’un corps d’accesseur, on utilise la signature de la partie d’implémentation. La signature de la partie définition est utilisée lors de l’analyse des sites d’utilisation et lors de l’émission. Cela est cohérent avec les méthodes partielles.
partial class C1
{
    public partial string Prop { get; private set; }

    // Error: accessor modifier mismatch in 'set' accessor of 'Prop'
    public partial string Prop { get => field; set => field = value; }
}

partial class C2
{
    public partial string Prop { get; init; }

    // Error: implementation of 'Prop' must have an 'init' accessor to match definition
    public partial string Prop { get => field; set => field = value; }
}

partial class C3
{
    public partial string Prop { get; }

    // Error: implementation of 'Prop' cannot have a 'set' accessor because the definition does not have a 'set' accessor.
    public partial string Prop { get => field; set => field = value; }
}

partial class C4
{
    public partial string this[string s = "a"] { get; set; }
    public partial string this[string s] { get => s; set { } } // ok

    public partial string this[int i, string s = "a"] { get; set; }
    public partial string this[int i, string s = "a"] { get => s; set { } } // CS1066: The default value specified for parameter 's' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments
}

Commentaires de documentation

Nous voulons que le comportement des commentaires de la documentation sur les propriétés partielles soit cohérent avec ce que nous avons fourni pour les méthodes partielles. Ce comportement est détaillé dans https://github.com/dotnet/csharplang/issues/5193.

Il est autorisé à inclure des commentaires de document sur la définition ou la partie implémentation d’une propriété partielle. (Notez que les commentaires de la documentation ne sont pas pris en charge sur les accesseurs de propriétés.)

Lorsque des commentaires de document sont présents sur une seule partie de la propriété, ces commentaires de document sont utilisés normalement (exposés par ISymbol.GetDocumentationCommentXml(), écrits dans le fichier XML de documentation, etc.).

Lorsque des commentaires de document sont présents sur les deux parties, tous les commentaires de document sur le composant définition sont supprimés et seuls les commentaires de document sur la partie implémentation sont utilisés.

Par exemple, le programme suivant :

/// <summary>
/// My type
/// </summary>
partial class C
{
    /// <summary>Definition part comment</summary>
    /// <returns>Return value comment</returns>
    public partial int Prop { get; set; }
    
    /// <summary>Implementation part comment</summary>
    public partial int Prop { get => 1; set { } }
}

Résultats dans le fichier de documentation XML suivant :

<?xml version="1.0"?>
<doc>
    <assembly>
        <name>ConsoleApp1</name>
    </assembly>
    <members>
        <member name="T:C">
            <summary>
            My type
            </summary>
        </member>
        <member name="P:C.Prop">
            <summary>
            Implementation part comment
            </summary>
        </member>
    </members>
</doc>

Lorsque les noms de paramètres diffèrent entre les déclarations partielles, <paramref> éléments utilisent les noms de paramètres de la déclaration associée au commentaire de documentation dans le code source. Par exemple, un paramref sur un commentaire doc placé sur une déclaration d’implémentation fait référence aux symboles de paramètre de la déclaration d’implémentation à l’aide de leurs noms de paramètres. Cela est cohérent avec les méthodes partielles.

/// <summary>
/// My type
/// </summary>
partial class C
{
    public partial int this[int x] { get; set; }

    /// <summary>
    /// <paramref name="x"/> // warning CS1734: XML comment on 'C.this[int]' has a paramref tag for 'x', but there is no parameter by that name
    /// <paramref name="y"/> // ok. 'Go To Definition' will go to 'int y'.
    /// </summary>
    public partial int this[int y] { get => 1; set { } } // warning CS9256: Partial property declarations 'int C.this[int x]' and 'int C.this[int y]' have signature differences.
}

Résultats dans le fichier de documentation XML suivant :

<?xml version="1.0"?>
<doc>
    <assembly>
        <name>ConsoleApp1</name>
    </assembly>
    <members>
        <member name="T:C">
            <summary>
            My type
            </summary>
        </member>
        <member name="P:C.Item(System.Int32)">
            <summary>
            <paramref name="x"/> // warning CS1734: XML comment on 'C.this[int]' has a paramref tag for 'x', but there is no parameter by that name
            <paramref name="y"/> // ok. 'Go To Definition' will go to 'int y'.
            </summary>
        </member>
    </members>
</doc>

Cela peut prêter à confusion, car la signature de métadonnées utilise des noms de paramètres à partir de la partie définition. Il est recommandé de s’assurer que les noms de paramètres correspondent entre les parties pour éviter cette confusion.

Indexeurs

Après la réunion LDM du 2 novembre 2022, les indexeurs sont pris en charge avec cette fonctionnalité.

La grammaire des indexeurs est modifiée comme suit :

indexer_declaration
-    : attributes? indexer_modifier* indexer_declarator indexer_body
+    : attributes? indexer_modifier* 'partial'? indexer_declarator indexer_body
-    | attributes? indexer_modifier* ref_kind indexer_declarator ref_indexer_body
+    | attributes? indexer_modifier* 'partial'? ref_kind indexer_declarator ref_indexer_body
    ;

Les paramètres d’un indexeur partiel doivent être identiques dans toutes les déclarations selon les mêmes règles que celles spécifiées dans Signatures concordantes. La fusion d’attributs est réalisée pour des paramètres d’indexeur partiels.

partial class C
{
    public partial int this[int x] { get; set; }
    public partial int this[int x]
    {
        get => this._store[x];
        set => this._store[x] = value;
    }
}

// attribute merging
partial class C
{
    public partial int this[[Attr1] int x]
    {
        [Attr2] get;
        set;
    }

    public partial int this[[Attr3] int x]
    {
        get => this._store[x];
        [Attr4] set => this._store[x] = value;
    }

    // results in a merged member emitted to metadata:
    public int this[[Attr1, Attr3] int x]
    {
        [Attr2] get => this._store[x];
        [Attr4] set => this._store[x] = value;
    }
}

Problèmes ouverts

Autres types de membres

Un membre de la communauté a ouvert une discussion pour demander une assistance pour des événements partiels. Lors de la réunion LDM du 2 novembre 2022, nous avons décidé de reporter l’assistance pour les événements, en partie parce que personne ne l’avait demandé à l’époque. Nous aimerions peut-être revoir cette question, puisque cette demande est arrivée et qu’elle a duré plus d’un an depuis la dernière discussion.

Nous pourrions également aller encore plus loin en permettant des déclarations partielles de constructeurs, d'opérateurs, de champs, etc., mais il reste incertain si la charge de conception que cela impliquerait est justifiée, simplement parce que nous avons déjà mis en place des propriétés partielles.