Partager via


Extension des méthodes partielles

Remarque

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

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

Pour en savoir plus sur le processus d'adoption des speclets de fonctionnalité dans la norme du langage C#, consultez l'article sur les spécifications.

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

Récapitulatif

Cette proposition vise à supprimer toutes les restrictions relatives aux signatures des méthodes partial en C#. L'objectif est d'élargir l'ensemble des scénarios dans lesquels ces méthodes peuvent fonctionner avec les générateurs de sources et d'offrir une forme de déclaration plus générale pour les méthodes C#.

Voir aussi la spécification originale des méthodes partielles (§15.6.9).

Motivation

C# a un support limité pour les développeurs qui divisent les méthodes en déclarations et définitions / implémentations.

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);
}

L'un des comportements des méthodes partial est que lorsque la définition est absente, le langage efface simplement tous les appels à la méthode partial. Essentiellement, cela se comporte comme un appel à une méthode [Conditional] où la condition a été évaluée à faux.

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";
}

La motivation initiale de cette fonctionnalité était la génération de sources sous la forme de code généré par le concepteur. Les utilisateurs modifiaient constamment le code généré parce qu'ils voulaient en accrocher un aspect. Il s'agit notamment de certaines parties du processus de démarrage de Windows Forms, après l'initialisation des composants.

L'édition du code généré était sujette à erreur, car toute action obligeant le concepteur à régénérer le code entraînait l'effacement de la modification effectuée par l'utilisateur. La fonctionnalité de la méthode partial a réduit cette tension, car elle a permis aux concepteurs d'implémenter des hooks sous la forme de méthodes partial.

Les concepteurs pouvaient émettre des crochets comme partial void OnComponentInit() et les développeurs pouvaient définir des déclarations pour eux ou ne pas les définir. Dans les deux cas, même si le code généré compile, les développeurs intéressés par le processus peuvent s'y intégrer selon les besoins.

Cela signifie que les méthodes partielles sont soumises à plusieurs restrictions :

  1. Doit avoir un type de retour void.
  2. Ne peut pas avoir de paramètres out.
  3. Ne peut avoir aucune accessibilité (implicitement private).

Ces restrictions existent parce que le langage doit être capable d'émettre du code lorsque le site d'appel est effacé. Étant donné qu'ils peuvent être effacés, private est la seule accessibilité possible parce que le membre ne peut pas être exposé dans les métadonnées de l'assemblage. Ces restrictions servent également à limiter l'ensemble des scénarios dans lesquels les méthodes partial peuvent être appliquées.

La présente proposition vise à supprimer toutes les restrictions existantes concernant les méthodes partial. En fait, il faut les laisser avoir des paramètres out, des types de retour non vides ou n'importe quel type d'accessibilité. Ces déclarations partial auraient alors l'exigence supplémentaire qu'une définition doit exister. Cela signifie que le langage ne doit pas tenir compte de l'impact de l'effacement des sites d'appel.

Cela développerait l’ensemble des scénarios de générateur auxquels les méthodes partial pourraient participer et s'intégrerait bien à notre fonctionnalité de générateurs de code source. Par exemple, une expression régulière peut être définie à l'aide du modèle suivant :

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

Le développeur dispose ainsi d'un moyen déclaratif simple d'opter pour les générateurs et ces derniers disposent d'un ensemble de déclarations très facile à consulter dans le code source pour piloter la sortie générée.

Comparez cela à la difficulté qu'aurait un générateur à connecter le bout de code suivant.

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

}

Étant donné que le compilateur n’autorise pas les générateurs à modifier la connexion du code à ce schéma, cela serait pratiquement impossible pour les générateurs. Ils devraient avoir recours à la réflexion dans l'implémentation de IsMatch, ou demander aux utilisateurs de changer leurs sites d'appel vers une nouvelle méthode + refactoriser la regex pour passer le littéral de chaîne en tant qu'argument. C'est assez compliqué.

Conception détaillée

La langue changera pour permettre aux méthodes partial d’être annotées avec un modificateur d’accessibilité explicite. Cela signifie qu'ils peuvent être étiquetés comme private, public, etc ...

Lorsqu'une méthode partial comporte un modificateur d'accessibilité explicite, le langage exigera que la déclaration comporte une définition correspondante, même si l'accessibilité est 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() { }
}

En outre, le langage lèvera toutes les restrictions sur ce qui peut apparaître sur une méthode partial qui a un modificateur d'accessibilité explicite. Ces déclarations peuvent contenir des types de retour non vides, des paramètres out, un modificateur extern, etc. Ces signatures bénéficieront de toute l'expressivité du langage 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) { ... }
}

Cela permet explicitement aux méthodes partial de participer aux implémentations overrides et interface :

interface IStudent
{
    string GetName();
}

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

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

Le compilateur modifiera l'erreur qu'il émet lorsqu'une méthode partial contient un élément illégal pour dire essentiellement :

Impossible d'utiliser ref sur une méthode partial qui ne dispose pas d'une accessibilité explicite.

Cela permettra d'orienter les développeurs dans la bonne direction lors de l'utilisation de cette fonctionnalité.

Restrictions :

  • Les déclarations partial avec une accessibilité explicite doivent avoir une définition
  • partial Les déclarations et les signatures de définition doivent correspondre à tous les modificateurs de méthode et de paramètre. Les seuls aspects qui peuvent différer sont les noms des paramètres et les listes d'attributs (il ne s'agit pas d'une nouveauté, mais plutôt d'une exigence existante des méthodes partial).

Questions

partielle pour tous les membres

Étant donné que nous développons partial pour être plus convivial aux générateurs de code source, devrions-nous aussi l'étendre pour qu'il fonctionne sur tous les éléments d'une classe ? Par exemple, nous devrions pouvoir déclarer partial constructeurs, opérateurs, etc...

Résolution L’idée est solide, mais à ce stade de la planification de C# 9, nous essayons d’éviter le glissement inutile des fonctionnalités. Vous souhaitez résoudre le problème immédiat de l'extension de la fonctionnalité pour qu'elle fonctionne avec des générateurs de sources modernes.

L'extension de partial pour prendre en charge d'autres membres sera envisagée pour la version C# 10. Il semble probable que nous envisagerons cette extension. Cette proposition reste active, mais n'a pas encore été mise en œuvre.

Utiliser abstract au lieu de partial

L'essentiel de cette proposition consiste à s'assurer qu'une déclaration a une définition/implémentation correspondante. Faut-il utiliser abstract étant déjà un mot-clé de langage imposant au développeur de réfléchir à une implémentation ?

Résolution Cette question a fait l'objet d'une saine discussion, mais a finalement été rejetée. Oui, les exigences sont familières, mais les concepts sont sensiblement différents. Cela pourrait facilement faire croire au développeur qu'il crée des emplacements virtuels alors que ce n'est pas le cas.