Retours covariantsCovariant returns

RésuméSummary

Prennent en charge les types de retour covariants.Support covariant return types. En particulier, autorisez la substitution d’une méthode à déclarer un type de retour plus dérivé que la méthode qu’elle substitue, et de la même manière pour autoriser la substitution d’une propriété en lecture seule pour déclarer un type plus dérivé.Specifically, permit the override of a method to declare a more derived return type than the method it overrides, and similarly to permit the override of a read-only property to declare a more derived type. Les déclarations override qui apparaissent dans des types plus dérivés sont requises pour fournir un type de retour au moins aussi spécifique que celui qui apparaît dans les substitutions dans ses types de base.Override declarations appearing in more derived types would be required to provide a return type at least as specific as that appearing in overrides in its base types. Les appelants de la méthode ou de la propriété recevaient statiquement le type de retour plus affiné à partir d’un appel.Callers of the method or property would statically receive the more refined return type from an invocation.

MotivationMotivation

Il s’agit d’un modèle courant dans le code selon lequel différents noms de méthode doivent être inventés pour contourner la contrainte de langage que les substitutions doivent retourner au même type que la méthode substituée.It is a common pattern in code that different method names have to be invented to work around the language constraint that overrides must return the same type as the overridden method.

Cela peut être utile dans le modèle de fabrique.This would be useful in the factory pattern. Par exemple, dans la base de code Roslyn, nous aurionsFor example, in the Roslyn code base we would have

class Compilation ...
{
    public virtual Compilation WithOptions(Options options)...
}
class CSharpCompilation : Compilation
{
    public override CSharpCompilation WithOptions(Options options)...
}

Conception détailléeDetailed design

Il s’agit d’une spécification pour les types de retour covariants en C#.This is a specification for covariant return types in C#. Notre intention est de permettre à la substitution d’une méthode de retourner un type de retour plus dérivé que la méthode qu’elle substitue, et de la même façon d’autoriser la substitution d’une propriété en lecture seule pour retourner un type de retour plus dérivé.Our intent is to permit the override of a method to return a more derived return type than the method it overrides, and similarly to permit the override of a read-only property to return a more derived return type. Les appelants de la méthode ou de la propriété recevaient statiquement le type de retour plus affiné à partir d’un appel, et les substitutions apparaissant dans des types plus dérivés seraient nécessaires pour fournir un type de retour au moins aussi spécifique que celui qui apparaît dans les substitutions dans ses types de base.Callers of the method or property would statically receive the more refined return type from an invocation, and overrides appearing in more derived types would be required to provide a return type at least as specific as that appearing in overrides in its base types.


Substitution de méthode de classeClass Method Override

Contrainte existante sur les méthodes de substitution de classeThe existing constraint on class override methods

  • La méthode de substitution et la méthode de base substituée ont le même type de retour.The override method and the overridden base method have the same return type.

est modifié enis modified to

  • La méthode override doit avoir un type de retour convertible par une conversion d’identité ou (si la méthode a une valeur retournée et non un retour Ref) une conversion de référence implicite vers le type de retour de la méthode de base substituée.The override method must have a return type that is convertible by an identity conversion or (if the method has a value return - not a ref return) implicit reference conversion to the return type of the overridden base method.

Et les conditions supplémentaires suivantes sont ajoutées à cette liste :And the following additional requirements are appended to that list:

  • La méthode de substitution doit avoir un type de retour convertible par une conversion d’identité ou (si la méthode a une valeur retournée et non un retourde référence) une conversion de référence implicite vers le type de retour de chaque substitution de la méthode de base substituée qui est déclarée dans un type de base (direct ou indirect) de la méthode de substitution.The override method must have a return type that is convertible by an identity conversion or (if the method has a value return - not a ref return) implicit reference conversion to the return type of every override of the overridden base method that is declared in a (direct or indirect) base type of the override method.
  • Le type de retour de la méthode de substitution doit être au moins aussi accessible que la méthode de substitution (domaines d’accessibilité).The override method's return type must be at least as accessible as the override method (Accessibility domains).

Cette contrainte permet à une méthode de substitution dans une private classe d’avoir un private type de retour.This constraint permits an override method in a private class to have a private return type. Toutefois, une public méthode de substitution dans un public type doit avoir un public type de retour.However it requires a public override method in a public type to have a public return type.

Substitution de la propriété de classe et de l’indexeurClass Property and Indexer Override

Contrainte existante sur les propriétés de substitution de classeThe existing constraint on class override properties

Une déclaration de propriété de substitution doit spécifier exactement les mêmes modificateurs d’accessibilité et nom que la propriété héritée, et il doit y avoir une conversion d’identité entre le type de la substitution et la propriété héritée.An overriding property declaration shall specify the exact same accessibility modifiers and name as the inherited property, and there shall be an identity conversion between the type of the overriding and the inherited property. Si la propriété héritée n’a qu’un seul accesseur (autrement dit, si la propriété héritée est en lecture seule ou en écriture seule), la propriété de substitution doit inclure uniquement cet accesseur.If the inherited property has only a single accessor (i.e., if the inherited property is read-only or write-only), the overriding property shall include only that accessor. Si la propriété héritée inclut des accesseurs (c’est-à-dire que si la propriété héritée est en lecture-écriture), la propriété de substitution peut inclure un seul accesseur ou les deux accesseurs.If the inherited property includes both accessors (i.e., if the inherited property is read-write), the overriding property can include either a single accessor or both accessors.

est modifié enis modified to

Une déclaration de propriété de substitution doit spécifier exactement les mêmes modificateurs d’accessibilité et Name que la propriété héritée, et il doit y avoir une conversion d’identité ou (si la propriété héritée est en lecture seule et a une valeur Return-not a refReturn) une conversion de référence implicite du type de la propriété de substitution vers le type de la propriété héritée.An overriding property declaration shall specify the exact same accessibility modifiers and name as the inherited property, and there shall be an identity conversion or (if the inherited property is read-only and has a value return - not a ref return) implicit reference conversion from the type of the overriding property to the type of the inherited property. Si la propriété héritée n’a qu’un seul accesseur (autrement dit, si la propriété héritée est en lecture seule ou en écriture seule), la propriété de substitution doit inclure uniquement cet accesseur.If the inherited property has only a single accessor (i.e., if the inherited property is read-only or write-only), the overriding property shall include only that accessor. Si la propriété héritée inclut des accesseurs (c’est-à-dire que si la propriété héritée est en lecture-écriture), la propriété de substitution peut inclure un seul accesseur ou les deux accesseurs.If the inherited property includes both accessors (i.e., if the inherited property is read-write), the overriding property can include either a single accessor or both accessors. Le type de la propriété de substitution doit être au moins aussi accessible que la propriété de substitution (domaines d’accessibilité).The overriding property's type must be at least as accessible as the overriding property (Accessibility domains).


Le reste de la spécification préliminaire ci-dessous propose une extension supplémentaire des retours covariants de méthodes d’interface à prendre en compte ultérieurement.The remainder of the draft specification below proposes a further extension to covariant returns of interface methods to be considered later.

Substitution de la méthode d’interface, de la propriété et de l’indexeurInterface Method, Property, and Indexer Override

En ajoutant aux genres de membres autorisés dans une interface avec l’ajout de la fonctionnalité DIM en C# 8,0, nous ajoutons davantage de prise en charge des membres avec les override retours covariants.Adding to the kinds of members that are permitted in an interface with the addition of the DIM feature in C# 8.0, we further add support for override members along with covariant returns. Celles-ci suivent les règles des override membres telles qu’elles sont spécifiées pour les classes, avec les différences suivantes :These follow the rules of override members as specified for classes, with the following differences:

Le texte suivant dans les classes :The following text in classes:

La méthode substituée par une déclaration override est connue sous le nom de méthode de base substituée.The method overridden by an override declaration is known as the overridden base method. Pour une méthode de substitution M déclarée dans une classe C , la méthode de base substituée est déterminée en examinant chaque classe de base de C , en commençant par la classe de base directe de C et en continuant avec chaque classe de base directe, jusqu’à ce que dans un type de classe de base donné, au moins une méthode accessible ait la même signature que M après la substitution des arguments de type.For an override method M declared in a class C, the overridden base method is determined by examining each base class of C, starting with the direct base class of C and continuing with each successive direct base class, until in a given base class type at least one accessible method is located which has the same signature as M after substitution of type arguments.

reçoit la spécification correspondante pour les interfaces :is given the corresponding specification for interfaces:

La méthode substituée par une déclaration override est connue sous le nom de méthode de base substituée _.The method overridden by an override declaration is known as the *overridden base method _. Pour une méthode de substitution M déclarée dans une interface I , la méthode de base substituée est déterminée en examinant chaque interface de base directe ou indirecte de I , en collectant le jeu d’interfaces déclarant une méthode accessible qui a la même signature qu' M après la substitution des arguments de type.For an override method M declared in an interface I, the overridden base method is determined by examining each direct or indirect base interface of I, collecting the set of interfaces declaring an accessible method which has the same signature as M after substitution of type arguments. Si cet ensemble d’interfaces a un _most type dérivé *, vers lequel il existe une conversion d’identité ou de référence implicite à partir de chaque type de cet ensemble, et que ce type contient une déclaration de méthode unique, il s’agit de la méthode de base substituée.If this set of interfaces has a _most derived type*, to which there is an identity or implicit reference conversion from every type in this set, and that type contains a unique such method declaration, then that is the overridden base method.

Nous autorisons de manière similaire les override Propriétés et les indexeurs dans les interfaces comme spécifié pour les classes dans les accesseurs 15.7.6 Virtual, sealed, override et abstract.We similarly permit override properties and indexers in interfaces as specified for classes in 15.7.6 Virtual, sealed, override, and abstract accessors.

Recherche de nomName Lookup

La recherche de nom en présence de override déclarations de classe modifie actuellement le résultat de la recherche de nom en imposant sur les détails de membre trouvés à partir de la déclaration la plus dérivée override dans la hiérarchie de classes à partir du type du qualificateur de l’identificateur (ou this en l’absence de qualificateur).Name lookup in the presence of class override declarations currently modify the result of name lookup by imposing on the found member details from the most derived override declaration in the class hierarchy starting from the type of the identifier's qualifier (or this when there is no qualifier). Par exemple, dans 12.6.2.2 paramètres correspondants , nous avonsFor example, in 12.6.2.2 Corresponding parameters we have

Pour les méthodes et les indexeurs virtuels définis dans les classes, la liste de paramètres est choisie à partir de la première déclaration ou substitution du membre de fonction trouvé lors du démarrage avec le type statique du récepteur, et la recherche dans ses classes de base.For virtual methods and indexers defined in classes, the parameter list is picked from the first declaration or override of the function member found when starting with the static type of the receiver, and searching through its base classes.

pour cela, nous ajoutonsto this we add

Pour les méthodes et les indexeurs virtuels définis dans les interfaces, la liste de paramètres est prélevée à partir de la déclaration ou de la substitution du membre de fonction trouvé dans le type le plus dérivé parmi les types contenant la déclaration de substitution de la fonction membre.For virtual methods and indexers defined in interfaces, the parameter list is picked from the declaration or override of the function member found in the most derived type among those types containing the declaration of override of the function member. Il s’agit d’une erreur au moment de la compilation si aucun type unique n’existe.It is a compile-time error if no unique such type exists.

Pour le type de résultat d’un accès à une propriété ou un indexeur, le texte existantFor the result type of a property or indexer access, the existing text

  • Si I identifie une propriété d’instance, le résultat est un accès à une propriété avec une expression d’instance associée de E et un type associé qui est le type de la propriété.If I identifies an instance property, then the result is a property access with an associated instance expression of E and an associated type that is the type of the property. Si T est un type de classe, le type associé est choisi dans la première déclaration ou la substitution de la propriété trouvée lors du démarrage de T, et la recherche dans ses classes de base.If T is a class type, the associated type is picked from the first declaration or override of the property found when starting with T, and searching through its base classes.

est augmenté avecis augmented with

Si T est un type interface, le type associé est choisi dans la déclaration ou la substitution de la propriété trouvée dans le le plus dérivé de T ou ses interfaces de base directes ou indirectes.If T is an interface type, the associated type is picked from the declaration or override of the property found in the most derived of T or its direct or indirect base interfaces. Il s’agit d’une erreur au moment de la compilation si aucun type unique n’existe.It is a compile-time error if no unique such type exists.

Une modification similaire doit être apportée à l’accès à l' indexeur 12.7.7.3A similar change should be made in 12.7.7.3 Indexer access

Dans les expressions d’appel 12.7.6 , nous enrichissons le texte existantIn 12.7.6 Invocation expressions we augment the existing text

  • Sinon, le résultat est une valeur, avec un type associé du type de retour de la méthode ou du délégué.Otherwise, the result is a value, with an associated type of the return type of the method or delegate. Si l’appel est d’une méthode d’instance et que le récepteur est d’un type de classe T, le type associé est choisi dans la première déclaration ou la substitution de la méthode trouvée lors du démarrage avec T et la recherche dans ses classes de base.If the invocation is of an instance method, and the receiver is of a class type T, the associated type is picked from the first declaration or override of the method found when starting with T and searching through its base classes.

parwith

Si l’appel est d’une méthode d’instance et que le récepteur est d’un type d’interface T, le type associé est choisi dans la déclaration ou la substitution de la méthode trouvée dans l’interface la plus dérivée parmi T et ses interfaces de base directes et indirectes.If the invocation is of an instance method, and the receiver is of an interface type T, the associated type is picked from the declaration or override of the method found in the most derived interface from among T and its direct and indirect base interfaces. Il s’agit d’une erreur au moment de la compilation si aucun type unique n’existe.It is a compile-time error if no unique such type exists.

Implémentations d’interface implicitesImplicit Interface Implementations

Cette section de la spécificationThis section of the specification

Dans le cadre du mappage d’interface, un membre de classe A correspond à un membre d’interface B lorsque :For purposes of interface mapping, a class member A matches an interface member B when:

  • A et B sont des méthodes, et le nom, le type et les listes de paramètres formels de A et B sont identiques.A and B are methods, and the name, type, and formal parameter lists of A and B are identical.
  • A et B sont des propriétés, le nom et le type de A et B sont identiques et A ont les mêmes accesseurs que B ( A est autorisé à avoir des accesseurs supplémentaires s’il ne s’agit pas d’une implémentation de membre d’interface explicite).A and B are properties, the name and type of A and B are identical, and A has the same accessors as B (A is permitted to have additional accessors if it is not an explicit interface member implementation).
  • A et B sont des événements, et le nom et le type de A et B sont identiques.A and B are events, and the name and type of A and B are identical.
  • A et B sont des indexeurs, les listes de paramètres de type et formels de A et B sont identiques, et A ont les mêmes accesseurs que B ( A est autorisé à avoir des accesseurs supplémentaires s’il ne s’agit pas d’une implémentation de membre d’interface explicite).A and B are indexers, the type and formal parameter lists of A and B are identical, and A has the same accessors as B (A is permitted to have additional accessors if it is not an explicit interface member implementation).

est modifié comme suit :is modified as follows:

Dans le cadre du mappage d’interface, un membre de classe A correspond à un membre d’interface B lorsque :For purposes of interface mapping, a class member A matches an interface member B when:

  • A et B sont des méthodes, et les listes de noms et de paramètres formels de A et B sont identiques, et le type de retour de A est convertible en type de retour B via une identité de conversion de référence implicite vers le type de retour de B .A and B are methods, and the name and formal parameter lists of A and B are identical, and the return type of A is convertible to the return type of B via an identity of implicit reference convertion to the return type of B.
  • A et B sont des propriétés, le nom de A et B sont identiques, A a les mêmes accesseurs que B ( A est autorisé à avoir des accesseurs supplémentaires s’il ne s’agit pas d’une implémentation de membre d’interface explicite) et le type de A est convertible en type de retour B via une conversion d’identité ou, si A est une propriété ReadOnly, une conversion de référence implicite.A and B are properties, the name of A and B are identical, A has the same accessors as B (A is permitted to have additional accessors if it is not an explicit interface member implementation), and the type of A is convertible to the return type of B via an identity conversion or, if A is a readonly property, an implicit reference conversion.
  • A et B sont des événements, et le nom et le type de A et B sont identiques.A and B are events, and the name and type of A and B are identical.
  • A et B sont des indexeurs, les listes de paramètres formels de A et B sont identiques, A ont les mêmes accesseurs que B ( A est autorisé à avoir des accesseurs supplémentaires s’il ne s’agit pas d’une implémentation de membre d’interface explicite) et le type de A est convertible en type de retour B via une conversion d’identité ou, si A est un indexeur ReadOnly, une conversion de référence implicite.A and B are indexers, the formal parameter lists of A and B are identical, A has the same accessors as B (A is permitted to have additional accessors if it is not an explicit interface member implementation), and the type of A is convertible to the return type of B via an identity conversion or, if A is a readonly indexer, an implicit reference conversion.

Il s’agit techniquement d’une modification avec rupture, car le programme ci-dessous affiche « C1 ». M « aujourd’hui, mais cela imprimerait «C2 ». M» sous la révision proposée.This is technically a breaking change, as the program below prints "C1.M" today, but would print "C2.M" under the proposed revision.

using System;

interface I1 { object M(); }
class C1 : I1 { public object M() { return "C1.M"; } }
class C2 : C1, I1 { public new string M() { return "C2.M"; } }
class Program
{
    static void Main()
    {
        I1 i = new C2();
        Console.WriteLine(i.M());
    }
}

En raison de cette modification avec rupture, nous pouvons envisager de ne pas prendre en charge les types de retour covariants sur les implémentations implicites.Due to this breaking change, we might consider not supporting covariant return types on implicit implementations.

Contraintes sur l’implémentation de l’interfaceConstraints on Interface Implementation

Nous aurons besoin d’une règle qui indique qu’une implémentation d’interface explicite doit déclarer un type de retour non moins dérivé que le type de retour déclaré dans une substitution de ses interfaces de base.We will need a rule that an explicit interface implementation must declare a return type no less derived than the return type declared in any override in its base interfaces.

Implications en matière de compatibilité des APIAPI Compatibility Implications

TBDTBD

Problèmes ouvertsOpen Issues

La spécification n’indique pas comment l’appelant obtient le type de retour le plus affiné.The specification does not say how the caller gets the more refined return type. Vraisemblablement, cela se fait de manière similaire à la façon dont les appelants obtiennent les spécifications de paramètre de substitution les plus dérivées.Presumably that would be done in a way similar to the way that callers get the most derived override's parameter specifications.


Si nous disposons des interfaces suivantes :If we have the following interfaces:

interface I1 { I1 M(); }
interface I2 { I2 M(); }
interface I3: I1, I2 { override I3 M(); }

Notez que dans I3 , les méthodes I1.M() et I2.M() ont été fusionnées.Note that in I3, the methods I1.M() and I2.M() have been “merged”. Lors de l’implémentation de I3 , il est nécessaire de les implémenter ensemble.When implementing I3, it is necessary to implement them both together.

En général, nous avons besoin d’une implémentation explicite pour faire référence à la méthode d’origine.Generally, we require an explicit implementation to refer to the original method. La question est, dans une classeThe question is, in a class

class C : I1, I2, I3
{
    C IN.M();
}

Qu’est-ce que cela signifie ?What does that mean here? Que dois - je faire ?What should N be?

Je vous suggère de permettre l’implémentation I1.M I2.M de ou (mais pas les deux), et de le considérer comme une implémentation des deux.I suggest that we permit implementing either I1.M or I2.M (but not both), and treat that as an implementation of both.

InconvénientsDrawbacks

  • [] Chaque modification de langue doit payer pour elle-même.[ ] Every language change must pay for itself.
  • [] Nous devons nous assurer que les performances sont raisonnables, même dans le cas des hiérarchies d’héritage profond[ ] We should ensure that the performance is reasonable, even in the case of deep inheritance hierarchies
  • [] Nous devons nous assurer que les artefacts de la stratégie de traduction n’affectent pas la sémantique de la langue, même lors de la consommation du nouveau langage intermédiaire à partir des compilateurs anciens.[ ] We should ensure that artifacts of the translation strategy do not affect language semantics, even when consuming new IL from old compilers.

Autres solutionsAlternatives

Nous pourrions assouplir légèrement les règles de langage pour autoriser, dans la source,We could relax the language rules slightly to allow, in source,

abstract class Cloneable
{
    public abstract Cloneable Clone();
}

class Digit : Cloneable
{
    public override Cloneable Clone()
    {
        return this.Clone();
    }

    public new Digit Clone() // Error: 'Digit' already defines a member called 'Clone' with the same parameter types
    {
        return this;
    }
}

Questions non résoluesUnresolved questions

  • [] Comment les API qui ont été compilées pour utiliser cette fonctionnalité fonctionnent-elles dans des versions antérieures du langage ?[ ] How will APIs that have been compiled to use this feature work in older versions of the language?

Réunions de conceptionDesign meetings