Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Nota:
Este artículo es una especificación de características. La especificación actúa como documento de diseño de la característica. Incluye cambios de especificación propuestos, junto con la información necesaria durante el diseño y el desarrollo de la característica. Estos artículos se publican hasta que se finalizan los cambios de especificación propuestos y se incorporan en la especificación ECMA actual.
Puede haber algunas discrepancias entre la especificación de características y la implementación completada. Esas diferencias se recogen en las notas de la reunión de diseño de idioma (LDM) pertinentes.
Puede obtener más información sobre el proceso de adopción de especificaciones de características en el estándar del lenguaje C# en el artículo sobre las especificaciones.
Problema planteado por el experto: https://github.com/dotnet/csharplang/issues/9058
Resumen
Permitir que el partial
modificador en eventos y constructores separe las partes de declaración e implementación, de forma similar a métodos parciales y propiedades o indexadores parciales.
partial class C
{
partial C(int x, string y);
partial event Action<int, string> MyEvent;
}
partial class C
{
partial C(int x, string y) { }
partial event Action<int, string> MyEvent
{
add { }
remove { }
}
}
Motivación
C# ya admite métodos, propiedades e indizadores parciales. Faltan eventos parciales y constructores.
Los eventos parciales serían útiles para las bibliotecas de eventos débiles, donde el usuario podría escribir definiciones:
partial class C
{
[WeakEvent]
partial event Action<int, string> MyEvent;
void M()
{
RaiseMyEvent(0, "a");
}
}
Y un generador de origen proporcionado por la biblioteca proporcionaría implementaciones:
partial class C
{
private readonly WeakEvent _myEvent;
partial event Action<int, string> MyEvent
{
add { _myEvent.Add(value); }
remove { _myEvent.Remove(value); }
}
protected void RaiseMyEvent(int x, string y)
{
_myEvent.Invoke(x, y);
}
}
Los eventos parciales y los constructores parciales también serían útiles para generar código de interoperabilidad como en Xamarin , donde el usuario podría escribir definiciones parciales de constructores y eventos:
partial class AVAudioCompressedBuffer : AVAudioBuffer
{
[Export("initWithFormat:packetCapacity:")]
public partial AVAudioCompressedBuffer(AVAudioFormat format, uint packetCapacity);
[Export("create:")]
public partial event EventHandler Created;
}
Y el generador de origen generaría los enlaces (en Objective-C en este caso):
partial class AVAudioCompressedBuffer : AVAudioBuffer
{
[BindingImpl(BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
public partial AVAudioCompressedBuffer(AVAudioFormat format, uint packetCapacity) : base(NSObjectFlag.Empty)
{
// Call Objective-C runtime:
InitializeHandle(
global::ObjCRuntime.NativeHandle_objc_msgSendSuper_NativeHandle_UInt32(
this.SuperHandle,
Selector.GetHandle("initWithFormat:packetCapacity:"),
format.GetNonNullHandle(nameof(format)),
packetCapacity),
"initWithFormat:packetCapacity:");
}
public partial event EventHandler Created
{
add { /* ... */ }
remove { /* ... */ }
}
}
Diseño detallado
General
La sintaxis de declaración de eventos (§15.8.1) se extiende para permitir el partial
modificador:
event_declaration
- : attributes? event_modifier* 'event' type variable_declarators ';'
+ : attributes? event_modifier* 'partial'? 'event' type variable_declarators ';'
- | attributes? event_modifier* 'event' type member_name
+ | attributes? event_modifier* 'partial'? 'event' type member_name
'{' event_accessor_declarations '}'
;
La sintaxis de declaración del constructor de instancia (§15.11.1) se extiende para permitir el partial
modificador:
constructor_declaration
- : attributes? constructor_modifier* constructor_declarator constructor_body
+ : attributes? constructor_modifier* 'partial'? constructor_declarator constructor_body
;
Tenga en cuenta que hay una propuesta para permitir el partial
modificador en cualquier lugar entre modificadores, en lugar de solo como último (también para declaraciones de método, propiedad y tipo).
Se dice que una declaración de evento con el partial
modificador es una declaración de evento parcial y está asociada a uno o varios eventos parciales con los nombres especificados (tenga en cuenta que una declaración de evento sin descriptores de acceso puede definir varios eventos).
Se dice que una declaración de constructor con el partial
modificador es una declaración de constructor parcial y está asociada a un constructor parcial con la firma especificada.
Se dice que una declaración de evento parcial es una declaración de implementación cuando especifica o event_accessor_declarations
tiene el extern
modificador .
De lo contrario, es una declaración de definición.
Se dice que una declaración de constructor parcial es una declaración de definición cuando tiene un cuerpo de punto y coma y carece del extern
modificador.
En caso contrario, se trata de una declaración de implementación.
partial class C
{
// defining declarations
partial C();
partial C(int x);
partial event Action E, F;
// implementing declarations
partial C() { }
partial C(int x) { }
partial event Action E { add { } remove { } }
partial event Action F { add { } remove { } }
}
Solo la declaración de definición de un miembro parcial participa en la búsqueda y se considera en sitios de uso y para emitir los metadatos. (Excepto para los comentarios de documentación como se detalla a continuación). La firma de declaración de implementación se usa para el análisis que acepta valores NULL de los cuerpos asociados.
Un evento o constructor parcial:
- Solo se puede declarar como miembro de un tipo parcial.
- Debe tener una declaración de definición y otra de implementación.
- No se permite tener el
abstract
modificador . - No se puede implementar explícitamente un miembro de interfaz.
Un evento parcial no es similar a un campo (§15.8.2), es decir:
- No tiene ningún almacenamiento de respaldo ni descriptores de acceso generados por el compilador.
- Solo se puede usar en
+=
operaciones y-=
, no como un valor.
Una declaración de constructor parcial que define no puede tener un inicializador de constructor (: this()
o : base()
; §15.11.2).
Interrupción de análisis
Permitir el partial
modificador en más contextos es un cambio importante:
class C
{
partial F() => new partial(); // previously a method, now a constructor
@partial F() => new partial(); // workaround to keep it a method
}
class partial { }
Para simplificar el analizador de lenguaje, el partial
modificador se acepta en cualquier declaración similar a método (es decir, funciones locales y métodos de script de nivel superior), aunque no se especifiquen los cambios gramaticales explícitamente anteriores.
Atributos
Los atributos del evento o constructor resultantes son los atributos combinados de las declaraciones parciales en las posiciones correspondientes. Los atributos combinados se concatenan en un orden no especificado y no se quitan los duplicados.
El method
attribute_target (§22.3) se omite en declaraciones de eventos parciales.
Los atributos de descriptor de acceso solo se usan desde declaraciones de descriptor de acceso (que solo pueden estar presentes en la declaración de implementación).
Tenga en cuenta que param
y return
attribute_targets ya se omiten en todas las declaraciones de eventos.
El compilador omite los atributos de información del autor de la llamada en la declaración de implementación según lo especificado por la propuesta de propiedades parciales de la sección Atributos de información del autor de la llamada (tenga en cuenta que se aplica a todos los miembros parciales que incluyen eventos y constructores parciales).
Firmas
Ambas declaraciones de un miembro parcial deben tener firmas coincidentes similares a las propiedades parciales:
- Las diferencias de tipo y tipo ref entre declaraciones parciales que son significativas para el tiempo de ejecución dan lugar a un error en tiempo de compilación.
- Las diferencias en los nombres de elementos de tupla entre declaraciones parciales producen un error en tiempo de compilación.
- Las declaraciones deben tener los mismos modificadores, aunque los modificadores pueden aparecer en un orden diferente.
- Excepción: esto no se aplica al
extern
modificador que solo puede aparecer en la declaración de implementación.
- Excepción: esto no se aplica al
- Todas las demás diferencias sintácticas en las firmas de declaraciones parciales producen una advertencia en tiempo de compilación, con las siguientes excepciones:
- No es necesario que las listas de atributos coincidan como se ha descrito anteriormente.
- Las diferencias de contexto que aceptan valores NULL (por ejemplo, oblicuas frente a anotadas) no provocan advertencias.
- Los valores de parámetro predeterminados no necesitan coincidir, pero se notifica una advertencia cuando la declaración del constructor de implementación tiene valores de parámetro predeterminados (ya que solo la declaración de definición participa en la búsqueda).
- Se produce una advertencia cuando los nombres de parámetro difieren en la definición e implementación de declaraciones de constructor.
- Las diferencias de nulabilidad que no implican nulabilidad omitosa dan lugar a advertencias.
Comentarios de documentación
Se permite incluir comentarios de documento en la declaración de definición e implementación. Tenga en cuenta que los comentarios del documento no se admiten en los descriptores de acceso de eventos.
Cuando los comentarios de documento están presentes solo en una de las declaraciones de un miembro parcial, esos comentarios de documento se usan normalmente (que se muestran a través de las API de Roslyn, emitidas en el archivo XML de documentación).
Cuando los comentarios del documento están presentes en ambas declaraciones de un miembro parcial, se quitan todos los comentarios del documento sobre la declaración de definición y solo se usan los comentarios del documento sobre la declaración de implementación.
Cuando los nombres de parámetro difieren entre las declaraciones de un miembro parcial, paramref
los elementos usan los nombres de parámetro de la declaración asociada con el comentario de documentación en el código fuente.
Por ejemplo, un paramref
en un comentario de documento colocado en una declaración de implementación hace referencia a los símbolos de parámetro de la declaración de implementación mediante sus nombres de parámetro.
Esto puede resultar confuso, ya que la firma de metadatos usará nombres de parámetro de la declaración de definición.
Se recomienda asegurarse de que los nombres de parámetro coincidan entre las declaraciones de un miembro parcial para evitar esta confusión.
Preguntas abiertas
Tipos de miembros
¿Queremos eventos parciales, constructores, operadores, campos? Propondremos los dos primeros tipos de miembro, pero se podría considerar cualquier otro subconjunto.
También se pueden considerar constructores primarios parciales, por ejemplo, permitir que el usuario tenga la misma lista de parámetros en varias declaraciones de tipos parciales.
Ubicaciones de atributos
¿Se debe reconocer el especificador de destino de [method:]
atributo para eventos parciales (o simplemente las declaraciones de definición)?
A continuación, los atributos de descriptor de acceso resultantes serían la concatenación de method
atributos -targeting de los elementos de declaración (o simplemente de definición) más los atributos de autoestinación y method
-targeting de los descriptores de acceso de la declaración de implementación.
La combinación de atributos de diferentes tipos de declaración sería sin precedentes y, de hecho, la implementación actual de la coincidencia de atributos en Roslyn no admite eso.
También podemos considerar la posibilidad de reconocer [param:]
y [return:]
, no solo en eventos parciales, sino en todos los eventos de tipo campo y extern.
Para obtener más información, vea https://github.com/dotnet/roslyn/issues/77254.
C# feature specifications