Valores devueltos de covarianteCovariant returns
ResumenSummary
Admitir tipos de valor devuelto covariante.Support covariant return types. En concreto, permita que la invalidación de un método declare un tipo de valor devuelto más derivado que el método que invalida y, de forma similar, para permitir que la invalidación de una propiedad de solo lectura declare un tipo más derivado.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. Las declaraciones de invalidación que aparecen en tipos más derivados serían necesarias para proporcionar un tipo de valor devuelto al menos tan específico como el que aparece en las invalidaciones en sus tipos 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. Los autores de llamadas del método o la propiedad recibiría estáticamente el tipo de valor devuelto más refinado de una invocación.Callers of the method or property would statically receive the more refined return type from an invocation.
MotivaciónMotivation
Es un patrón común en el código que los distintos nombres de método deben inventar para solucionar la restricción de lenguaje que las invalidaciones deben devolver el mismo tipo que el método invalidado.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.
Esto sería útil en el patrón de fábrica.This would be useful in the factory pattern. Por ejemplo, en la base de código Roslyn deberíamos tenerFor 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)...
}
Diseño detalladoDetailed design
Esta es una especificación para los tipos de valor devuelto covariante en C#.This is a specification for covariant return types in C#. Nuestra intención es permitir que la invalidación de un método devuelva un tipo de valor devuelto más derivado que el método que invalida y, de forma similar, permitir que la invalidación de una propiedad de solo lectura devuelva un tipo de valor devuelto más derivado.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. Los autores de llamadas del método o la propiedad recibiría estáticamente el tipo de valor devuelto más refinado de una invocación, y las invalidaciones que aparecen en tipos más derivados serían necesarias para proporcionar un tipo de valor devuelto al menos tan específico como el que aparece en invalidaciones en sus tipos 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.
Invalidación del método de claseClass Method Override
Restricción existente en métodos de invalidación de claseThe existing constraint on class override methods
- El método de invalidación y el método base invalidado tienen el mismo tipo de valor devuelto.The override method and the overridden base method have the same return type.
se modifica ais modified to
- El método de invalidación debe tener un tipo de valor devuelto que sea convertible por una conversión de identidad o (si el método tiene un valor devuelto, no una devoluciónde referencia), al tipo de valor devuelto del método base invalidado.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.
Y los siguientes requisitos adicionales se anexan a la lista:And the following additional requirements are appended to that list:
- El método de invalidación debe tener un tipo de valor devuelto que sea convertible por una conversión de identidad o (si el método tiene un valor devuelto, no una devoluciónde referencia) al tipo de valor devuelto de cada invalidación del método base invalidado que se declara en un tipo base (directa o indirecta) del método de invalidación.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.
- El tipo de valor devuelto del método de invalidación debe ser al menos igual de accesible que el método de invalidación (dominios de accesibilidad).The override method's return type must be at least as accessible as the override method (Accessibility domains).
Esta restricción permite que un método de invalidación de una private clase tenga un private tipo de valor devuelto.This constraint permits an override method in a private class to have a private return type. Sin embargo, requiere un public método de invalidación en un public tipo para tener un public tipo de valor devuelto.However it requires a public override method in a public type to have a public return type.
Invalidación de propiedad de clase e indexadorClass Property and Indexer Override
Restricción existente en las propiedades de invalidación de claseThe existing constraint on class override properties
Una declaración de propiedad de reemplazo debe especificar exactamente los mismos modificadores de accesibilidad y el mismo nombre que la propiedad heredada, y debe haber una conversión
de identidad entre el tipo de reemplazo y la propiedad heredada.An overriding property declaration shall specify the exact same accessibility modifiers and name as the inherited property, and there shall be an identity conversionbetween the type of the overriding and the inherited property. Si la propiedad heredada solo tiene un descriptor de acceso (es decir, si la propiedad heredada es de solo lectura o de solo escritura), la propiedad de reemplazo solo incluirá ese descriptor de acceso.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 propiedad heredada incluye ambos descriptores de acceso (es decir, si la propiedad heredada es de lectura y escritura), la propiedad de reemplazo puede incluir un solo descriptor de acceso o ambos.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.
se modifica ais modified to
Una declaración de propiedad de reemplazo debe especificar exactamente los mismos modificadores de accesibilidad y el mismo nombre que la propiedad heredada, y debe haber una conversión de identidad o (si la propiedad heredada es de solo lectura y tiene un valor devuelto, no una devoluciónde referencia) conversión implícita del tipo de la propiedad de reemplazo al tipo de la propiedad heredada.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 propiedad heredada solo tiene un descriptor de acceso (es decir, si la propiedad heredada es de solo lectura o de solo escritura), la propiedad de reemplazo solo incluirá ese descriptor de acceso.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 propiedad heredada incluye ambos descriptores de acceso (es decir, si la propiedad heredada es de lectura y escritura), la propiedad de reemplazo puede incluir un solo descriptor de acceso o ambos.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. El tipo de la propiedad de reemplazo debe ser al menos igual de accesible que la propiedad de reemplazo (dominios de accesibilidad).The overriding property's type must be at least as accessible as the overriding property (Accessibility domains).
El resto de la especificación del borrador siguiente propone una extensión adicional a las devoluciones de covariantes de los métodos de interfaz que se deben considerar más adelante.The remainder of the draft specification below proposes a further extension to covariant returns of interface methods to be considered later.
Invalidación del método de interfaz, la propiedad y el indexadorInterface Method, Property, and Indexer Override
Al agregar a los tipos de miembros que se permiten en una interfaz con la adición de la característica DIM en C# 8,0, se agrega compatibilidad adicional a override los miembros junto con las devoluciones covariantes.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. Estos siguen las reglas de override los miembros especificados para las clases, con las siguientes diferencias:These follow the rules of override members as specified for classes, with the following differences:
El siguiente texto en las clases:The following text in classes:
El método invalidado por una declaración de invalidación se conoce como el método base invalidado.The method overridden by an override declaration is known as the overridden base method. Para un método de invalidación
Mdeclarado en una claseC, el método base invalidado se determina examinando cada clase base deC, empezando por la clase base directa deCy continuando con cada clase base directa sucesiva, hasta que en un tipo de clase base determinado se encuentra al menos un método accesible que tiene la misma firma queMdespués de la sustitución de los argumentos de tipo.For an override methodMdeclared in a classC, the overridden base method is determined by examining each base class ofC, starting with the direct base class ofCand 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 asMafter substitution of type arguments.
se proporciona la especificación correspondiente para las interfaces:is given the corresponding specification for interfaces:
El método invalidado por una declaración de invalidación se conoce como el *método base invalidado _.The method overridden by an override declaration is known as the *overridden base method _. Para un método de invalidación
Mdeclarado en una interfazI, el método base invalidado se determina examinando cada interfaz base directa o indirecta deI, recopilando el conjunto de interfaces que declaran un método accesible que tiene la misma firma queMdespués de la sustitución de los argumentos de tipo.For an override methodMdeclared in an interfaceI, the overridden base method is determined by examining each direct or indirect base interface ofI, collecting the set of interfaces declaring an accessible method which has the same signature asMafter substitution of type arguments. Si este conjunto de interfaces tiene un _most tipo derivado *, en el que hay una conversión de referencia implícita o de identidad de cada tipo de este conjunto, y ese tipo contiene una declaración de método única de este tipo, es el método base invalidado.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.
De igual forma, se permiten override Propiedades e indexadores en interfaces, tal y como se especifica para las clases de descriptores de acceso virtual, sellado, invalidación y Abstract de 15.7.6.We similarly permit override properties and indexers in interfaces as specified for classes in 15.7.6 Virtual, sealed, override, and abstract accessors.
Búsqueda de nombreName Lookup
La búsqueda de nombres en la presencia de las declaraciones de clase override modifica actualmente el resultado de la búsqueda de nombres mediante la imposición de los detalles del miembro encontrado de la declaración más derivada override en la jerarquía de clases a partir del tipo del calificador del identificador (o cuando no hay this ningún calificador).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). Por ejemplo, en 12.6.2.2 parámetros correspondientes , tenemosFor example, in 12.6.2.2 Corresponding parameters we have
En el caso de los métodos virtuales e indexadores definidos en las clases, se selecciona la lista de parámetros de la primera declaración o invalidación del miembro de función que se encuentra al iniciar con el tipo estático del receptor y buscar a través de sus clases 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.
para ello, agregamosto this we add
En el caso de los métodos virtuales e indexadores definidos en las interfaces, la lista de parámetros se selecciona de la declaración o invalidación del miembro de función que se encuentra en el tipo más derivado entre los tipos que contienen la declaración de invalidación del miembro de función.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. Es un error en tiempo de compilación si no existe ningún tipo de este tipo único.It is a compile-time error if no unique such type exists.
Para el tipo de resultado de un acceso de propiedad o indizador, el texto existenteFor the result type of a property or indexer access, the existing text
- Si I identifica una propiedad de instancia, el resultado es un acceso de propiedad con una expresión de instancia asociada de E y un tipo asociado que es el tipo de la propiedad.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 es un tipo de clase, se selecciona el tipo asociado de la primera declaración o invalidación de la propiedad encontrada al comenzar con T y buscando en sus clases 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.
se aumenta conis augmented with
Si T es un tipo de interfaz, el tipo asociado se selecciona de la declaración o la invalidación de la propiedad que se encuentra en la más derivada de T o sus interfaces base directas o indirectas.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. Es un error en tiempo de compilación si no existe ningún tipo de este tipo único.It is a compile-time error if no unique such type exists.
Se debe realizar un cambio similar en el acceso del indexador de 12.7.7.3A similar change should be made in 12.7.7.3 Indexer access
En las expresiones de invocación de 12.7.6 , aumentamos el texto existente.In 12.7.6 Invocation expressions we augment the existing text
- De lo contrario, el resultado es un valor, con un tipo asociado del tipo de valor devuelto del método o delegado.Otherwise, the result is a value, with an associated type of the return type of the method or delegate. Si la invocación es de un método de instancia y el receptor es de un tipo de clase T, se selecciona el tipo asociado de la primera declaración o invalidación del método encontrado al iniciar con T y buscar a través de sus clases 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.
conwith
Si la invocación es de un método de instancia y el receptor es de un tipo de interfaz T, se selecciona el tipo asociado de la declaración o invalidación del método encontrado en la interfaz más derivada desde T y sus interfaces base directas e indirectas.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. Es un error en tiempo de compilación si no existe ningún tipo de este tipo único.It is a compile-time error if no unique such type exists.
Implementaciones de interfaz IMPLÍCITASImplicit Interface Implementations
Esta sección de la especificaciónThis section of the specification
En lo que respecta a la asignación de interfaz, un miembro de clase
Acoincide con un miembro de interfazBcuando:For purposes of interface mapping, a class memberAmatches an interface memberBwhen:
AyBson métodos, y el nombre, el tipo y las listas de parámetros formales deAyBson idénticos.AandBare methods, and the name, type, and formal parameter lists ofAandBare identical.AyBson propiedades, el nombre y el tipo deAyBson idénticos, yAtienen los mismos descriptores de acceso queB(Ase permite tener descriptores de acceso adicionales si no es una implementación de miembro de interfaz explícita).AandBare properties, the name and type ofAandBare identical, andAhas the same accessors asB(Ais permitted to have additional accessors if it is not an explicit interface member implementation).AyBson eventos, y el nombre y el tipo deAyBson idénticos.AandBare events, and the name and type ofAandBare identical.AyBson indizadores, las listas de parámetros de tipo y formales deAyBson idénticas, yAtienen los mismos descriptores de acceso queB(Ase le permite tener descriptores de acceso adicionales si no es una implementación de miembro de interfaz explícita).AandBare indexers, the type and formal parameter lists ofAandBare identical, andAhas the same accessors asB(Ais permitted to have additional accessors if it is not an explicit interface member implementation).
se modifica de la siguiente manera:is modified as follows:
En lo que respecta a la asignación de interfaz, un miembro de clase
Acoincide con un miembro de interfazBcuando:For purposes of interface mapping, a class memberAmatches an interface memberBwhen:
AyBson métodos, y el nombre y las listas de parámetros formales deAyBson idénticos, y el tipo de valor devuelto deAse pueden convertir al tipo de valor devuelto deBa través de una identidad de conversión de referencia implícita al tipo de valor devuelto deB.AandBare methods, and the name and formal parameter lists ofAandBare identical, and the return type ofAis convertible to the return type ofBvia an identity of implicit reference convertion to the return type ofB.AyBson propiedades, el nombre deAyBson idénticos,Atiene los mismos descriptores de acceso queB(Ase le permite tener descriptores de acceso adicionales si no es una implementación explícita de un miembro de interfaz) y el tipo deAse puede convertir al tipo de valor devuelto deBa través de una conversión de identidad o, siAes una propiedad ReadOnly, una conversión de referencia implícita.AandBare properties, the name ofAandBare identical,Ahas the same accessors asB(Ais permitted to have additional accessors if it is not an explicit interface member implementation), and the type ofAis convertible to the return type ofBvia an identity conversion or, ifAis a readonly property, an implicit reference conversion.AyBson eventos, y el nombre y el tipo deAyBson idénticos.AandBare events, and the name and type ofAandBare identical.AyBson indizadores, las listas de parámetros formales deAyBson idénticas,Atiene los mismos descriptores de acceso queB(Ase le permite tener descriptores de acceso adicionales si no es una implementación explícita de un miembro de interfaz) y el tipo deAse puede convertir al tipo de valor devuelto deBa través de una conversión de identidad o, siAes un indexador de solo lecturaAandBare indexers, the formal parameter lists ofAandBare identical,Ahas the same accessors asB(Ais permitted to have additional accessors if it is not an explicit interface member implementation), and the type ofAis convertible to the return type ofBvia an identity conversion or, ifAis a readonly indexer, an implicit reference conversion.
Técnicamente, se trata de un cambio importante, ya que el programa siguiente imprime "C1. M "hoy, pero imprimiría" C2. M "en la revisión propuesta.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());
}
}
Debido a este cambio importante, podríamos considerar no admitir tipos de valor devuelto covariante en implementaciones implícitas.Due to this breaking change, we might consider not supporting covariant return types on implicit implementations.
Restricciones en la implementación de la interfazConstraints on Interface Implementation
Se necesitará una regla de que una implementación de interfaz explícita debe declarar un tipo de valor devuelto no menos derivado que el tipo de valor devuelto declarado en cualquier invalidación en sus interfaces 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.
Implicaciones de compatibilidad de APIAPI Compatibility Implications
TBDTBD
Problemas abiertosOpen Issues
La especificación no indica cómo el autor de la llamada obtiene el tipo de valor devuelto más refinado.The specification does not say how the caller gets the more refined return type. Presumiblemente esto se haría de manera similar a la forma en que los llamadores obtienen las especificaciones de parámetro de la invalidación más derivada.Presumably that would be done in a way similar to the way that callers get the most derived override's parameter specifications.
Si tenemos las siguientes interfaces:If we have the following interfaces:
interface I1 { I1 M(); }
interface I2 { I2 M(); }
interface I3: I1, I2 { override I3 M(); }
Tenga en cuenta que en I3 , los métodos I1.M() y I2.M() se han "combinado".Note that in I3, the methods I1.M() and I2.M() have been “merged”. Al implementar I3 , es necesario implementar ambos juntos.When implementing I3, it is necessary to implement them both together.
Por lo general, se requiere una implementación explícita para hacer referencia al método original.Generally, we require an explicit implementation to refer to the original method. La pregunta es, en una claseThe question is, in a class
class C : I1, I2, I3
{
C IN.M();
}
¿Qué significa esto?What does that mean here? ¿Qué debe hacer?What should N be?
Se recomienda que permita implementar I1.M o I2.M (pero no ambos) y tratarlo como una implementación de ambos.I suggest that we permit implementing either I1.M or I2.M (but not both), and treat that as an implementation of both.
InconvenientesDrawbacks
- [] Todos los cambios de idioma deben pagarse por sí mismos.[ ] Every language change must pay for itself.
- [] Debemos asegurarnos de que el rendimiento sea razonable, incluso en el caso de las jerarquías de herencia profunda.[ ] We should ensure that the performance is reasonable, even in the case of deep inheritance hierarchies
- [] Debemos asegurarnos de que los artefactos de la estrategia de traslación no afecten a la semántica del lenguaje, incluso cuando se utiliza un nuevo IL de compiladores antiguos.[ ] We should ensure that artifacts of the translation strategy do not affect language semantics, even when consuming new IL from old compilers.
AlternativasAlternatives
Podríamos relajar las reglas de idioma ligeramente para permitir, en origen,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;
}
}
Preguntas no resueltasUnresolved questions
- [] ¿Cómo funcionarán las API que se han compilado para usar esta característica en versiones anteriores del lenguaje?[ ] How will APIs that have been compiled to use this feature work in older versions of the language?
Design MeetingsDesign meetings
- algunos debates en https://github.com/dotnet/roslyn/issues/357 .some discussion at https://github.com/dotnet/roslyn/issues/357.
- https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-01-08.md
- Debate sin conexión sobre una decisión de admitir la invalidación de métodos de clase solo en C# 9,0.Offline discussion toward a decision to support overriding of class methods only in C# 9.0.