Parámetros ref readonly
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 lenguaje (LDM) correspondientes.
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 especificaciones.
Problema planteado por el experto: https://github.com/dotnet/csharplang/issues/6010
Resumen
Permita el modificador del sitio de la declaración de parámetros ref readonly
y cambie las reglas de llamada de la siguiente manera:
Anotación de sitio de llamada (callsite) | Parámetro ref |
Parámetro ref readonly |
Parámetro in |
Parámetro out |
---|---|---|---|---|
ref |
Permitido | Permitido | Advertencia | Error |
in |
Error | Permitido | Permitido | Error |
out |
Error | Error | Error | Permitido |
Sin anotación | Error | Advertencia | Permitido | Error |
(Tenga en cuenta que hay un cambio en las reglas existentes: el parámetro in
con la anotación del sitio de llamada ref
genera una advertencia en lugar de un error).
Cambie las reglas de valor de argumento de la siguiente manera:
Tipo de valor | Parámetro ref |
Parámetro ref readonly |
Parámetro in |
Parámetro out |
---|---|---|---|---|
rvalue | Error | Advertencia | Permitido | Error |
lvalue | Permitido | Permitido | Permitido | Permitido |
Donde lvalue significa una variable (es decir, un valor con una ubicación; no tiene que admitir escritura/asignación) y rvalue significa cualquier tipo de valor.
Motivación
C# 7.2 ha incluido los parámetros in
como una forma de pasar referencias de solo lectura.
Los parámetrosin
permiten valores lvalue y rvalue y se pueden usar sin ninguna anotación en el sitio de llamada.
Sin embargo, las API que capturan o devuelven referencias de sus parámetros no permiten los valores rvalues y también aplican alguna indicación en el objeto del sitio de llamada que esté capturando una referencia.
Los parámetros ref readonly
son ideales en tales casos, ya que advierten si se usan con rvalues o sin ninguna anotación en el callsite.
Además, hay API que solo necesitan referencias de lectura, pero usan:
- parámetros
ref
desde que se incluyeron antes de quein
estuviera disponible y de que el cambio ain
fuera un cambio significativo de código fuente y binario, por ejemplo,QueryInterface
, o - parámetros
in
que aceptan referencias de solo lectura, aunque realmente no tenga sentido valores rvalues, por ejemplo,ReadOnlySpan<T>..ctor(in T value)
, o - parámetros
ref
que prohíben rvalues aunque no modifican la referencia pasada, por ejemplo,Unsafe.IsNullRef
.
Estas API podrían migrar a parámetros ref readonly
sin interrumpir a los usuarios.
Para obtener más información sobre la compatibilidad binaria, consulte la codificación de metadatos propuesta.
En concreto, si se cambia:
ref
→ref readonly
solo significaría un cambio importante de código binario en los métodos virtuales,ref
→in
también sería un cambio binario rompedor para los métodos virtuales, pero no un cambio rompedor de código fuente (porque las reglas cambian para advertir únicamente sobre los argumentos deref
pasados a parámetros dein
),in
→ref readonly
no sería un cambio importante (pero ni la anotación del punto de llamada ni un valor r provocarían una advertencia),- tenga en cuenta que esto sería un cambio de código fuente para los usuarios que usen versiones antiguas del compilador (ya que interpretan los parámetros
ref readonly
como parámetrosref
, lo que no permite el uso dein
o la ausencia de anotaciones en el sitio de llamada) y las nuevas versiones del compilador conLangVersion <= 11
(por coherencia con las versiones anteriores, se generará un error indicando que los parámetrosref readonly
no son compatibles a menos que los argumentos correspondientes se pasen con el modificadorref
).
- tenga en cuenta que esto sería un cambio de código fuente para los usuarios que usen versiones antiguas del compilador (ya que interpretan los parámetros
En el sentido contrario, si se cambia:
ref readonly
→ref
podría suponer un cambio potencialmente significativo de código fuente (a menos que solo se usara la anotación del sitio de llamada deref
y solo se usaran referencias de solo lectura como argumentos) y como un cambio significativo a nivel binario en los métodos virtuales,-
ref readonly
→in
no sería un cambio importante (pero la anotación del sitio de llamada deref
generaría una advertencia).
Tenga en cuenta que las reglas descritas anteriormente se aplican a las firmas de método, pero no a firmas de delegados.
Por ejemplo, si se cambia ref
por in
en una firma de delegado, esto puede suponer un cambio significativo (si un usuario asignara un método con un parámetro ref
a ese tipo de delegado, se generaría en un error después del cambio de la API).
Diseño detallado
En general, las reglas para los parámetros de
Declaraciones de parámetros
No es necesario realizar ningún cambio en la gramática.
El modificador ref readonly
se permite en los parámetros.
Aparte de los métodos normales, se permitirá ref readonly
para los parámetros del indexador (como in
, pero a diferencia de ref
), pero no se permite para los parámetros del operador (como ref
, pero a diferencia de in
).
Los valores de parámetro predeterminados se permitirán para los parámetros ref readonly
, pero emitirán una advertencia ya que son equivalentes a pasar valores R.
Esto permite a los autores de la API cambiar parámetros con valores predeterminados in
a parámetros ref readonly
sin introducir un cambio que rompa la compatibilidad del código fuente.
Comprobaciones de tipo de valor
Tenga en cuenta que, aunque se permite el modificador de argumentos ref
para los parámetros ref readonly
, no cambia nada con respecto a las comprobaciones del tipo de valor, es decir,
-
ref
solo se pueden usar con valores asignables; - para pasar referencias de solo lectura, se tiene que usar el modificador de argumento
in
; - para pasar rvalues, no se tiene que usar ningún modificador (lo que da genera una advertencia en los parámetros
ref readonly
, tal como se describe en el resumen de esta propuesta).
Resolución de sobrecarga
La resolución de sobrecarga permitirá mezclar anotaciones de sitio de llamada ref
/ref readonly
/in
y modificadores de parámetros, tal como se indica en la tabla del resumen de esta propuesta. Por ejemplo, se considerarán posibles candidatos durante la resolución de sobrecarga todos los elementos permitidos y con advertencia.
En concreto, hay un cambio en el funcionamiento por el que los métodos con el parámetro in
emparejan las llamadas con el argumento correspondiente marcado como ref
; este cambio se controla en LangVersion.
Sin embargo, se suprimirá la advertencia para pasar un argumento sin modificador de sitio de llamada a un parámetro ref readonly
si el parámetro es
- el receptor en una invocación de método de extensión,
- se usa implícitamente como parte de un inicializador de colección personalizado o de un controlador de cadenas interpoladas.
Las sobrecargas por valor se preferirán sobre las sobrecargas ref readonly
en caso de que no haya ningún modificador de argumento (los parámetrosin
tienen el mismo comportamiento).
Conversiones de métodos
Del mismo modo, para las conversiones de funciones anónimas [§10.7] y de grupos de métodos [§10.8], estos modificadores se consideran compatibles (aunque cualquier conversión permitida entre diferentes modificadores resulta en una advertencia):
- el parámetro
ref readonly
del método de destino puede coincidir con el parámetroin
oref
del delegado, - Se permite que el parámetro
in
del método de destino coincida con el parámetroref readonly
o, dependiendo de la versión del lenguaje, con el parámetroref
del delegado. - Nota: El parámetro
ref
del método de destino no tiene permitido coincidir con el parámetroin
ni con el parámetroref readonly
del delegado.
Por ejemplo:
DIn dIn = (ref int p) => { }; // error: cannot match `ref` to `in`
DRef dRef = (in int p) => { }; // warning: mismatch between `in` and `ref`
DRR dRR = (ref int p) => { }; // error: cannot match `ref` to `ref readonly`
dRR = (in int p) => { }; // warning: mismatch between `in` and `ref readonly`
dIn = (ref readonly int p) => { }; // warning: mismatch between `ref readonly` and `in`
dRef = (ref readonly int p) => { }; // warning: mismatch between `ref readonly` and `ref`
delegate void DIn(in int p);
delegate void DRef(ref int p);
delegate void DRR(ref readonly int p);
Tenga en cuenta que no hay ningún cambio en el proceso de las conversiones de punteros de función. Le recordamos que las conversiones de punteros de función implícitas no se permiten si hay una discrepancia entre los modificadores de tipo de referencia. Además, las conversiones explícitas siempre se permiten sin advertencias.
Coincidencia de firmas
Los miembros declarados en un solo tipo no pueden diferir en la firma únicamente por ref
/out
/in
/ref readonly
.
En otras situaciones para emparejar firmas (por ejemplo, ocultar o invalidar), ref readonly
se puede intercambiar por el modificador in
, pero esto genera en una advertencia en el sitio de la declaración [§7.6].
Esto no se aplica al cotejar la declaración partial
con su implementación y al cotejar la firma del interceptor con la firma interceptada.
Tenga en cuenta que no hay ningún cambio en la sobrescritura de los pares de modificadores ref
/in
y ref readonly
/ref
, ya que no se pueden intercambiar porque las firmas no son compatibles de manera binaria.
Por motivos de coherencia, lo mismo ocurre con otros fines de coincidencia de firmas (por ejemplo, ocultar).
Codificación de metadatos
Como recordatorio,
- los parámetros
ref
se emiten como tipos de byref sin formato (T&
en IL), - Los
in
parámetros son comoref
, además están anotados conSystem.Runtime.CompilerServices.IsReadOnlyAttribute
. En C# 7.3 y versiones posteriores, también se emiten con[in]
y, si es virtual,modreq(System.Runtime.InteropServices.InAttribute)
.
Los parámetros ref readonly
se emitirán como [in] T&
y además se anotarán con el siguiente atributo.
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
public sealed class RequiresLocationAttribute : Attribute
{
}
}
Además, si son virtuales, se emitirán con modreq(System.Runtime.InteropServices.InAttribute)
para garantizar la compatibilidad binaria con los parámetros in
.
Es importante destacar que, a diferencia de los parámetros in
, no se emitirá ningún [IsReadOnly]
para los parámetros ref readonly
, con el fin de evitar incrementar el tamaño de los metadatos y permitir que versiones anteriores del compilador interpreten los parámetros ref readonly
como parámetros ref
(y, por lo tanto, ref
→ ref readonly
no será un cambio drástico en el código fuente, incluso entre diferentes versiones del compilador).
El RequiresLocationAttribute
coincidirá con el nombre calificado por el espacio de nombres correspondiente y se sintetizará en el compilador si aún no está incluido en la compilación.
Si se especifica el atributo en el origen, se producirá un error si se aplica a un parámetro, de forma similar a ParamArrayAttribute
.
Punteros de función
En los punteros de función, los parámetros in
se emiten con modreq(System.Runtime.InteropServices.InAttribute)
(consulte la propuesta de punteros de función).
los parámetros ref readonly
se emitirán sin ese modreq
y en su lugar lo harán con modopt(System.Runtime.CompilerServices.RequiresLocationAttribute)
.
Las versiones anteriores del compilador omitirán modopt
y, por tanto, interpretarán los parámetros ref readonly
como parámetros ref
(coherentes con el comportamiento anterior del compilador para los métodos normales con parámetros ref readonly
tal como se ha descrito anteriormente) y las nuevas versiones del compilador que conocen modopt
lo usarán para reconocer parámetros ref readonly
para emitir advertencias durante las conversiones e invocaciones.
Para mantener la coherencia con versiones anteriores del compilador, las nuevas versiones del compilador con LangVersion <= 11
informarán de errores indicando que los parámetros ref readonly
no son compatibles, a menos que los argumentos correspondientes se pasen con el modificador ref
.
Tenga en cuenta que es una interrupción de código binario para cambiar los modificadores en firmas de punteros de función si forman parte de API públicas, por lo que será una interrupción binaria al cambiar ref
o in
por ref readonly
.
Sin embargo, solo se producirá una interrupción del código fuente para los autores de llamadas con LangVersion <= 11
al cambiar in
→ ref readonly
(si se invoca el puntero con el modificador del sitio de llamada in
), que se corresponda con los métodos normales.
Cambios importantes
La relajación de discrepancias de ref
/in
en la resolución de sobrecarga implica un cambio importante de funcionamiento que se refleja en el ejemplo siguiente:
class C
{
string M(in int i) => "C";
static void Main()
{
int i = 5;
System.Console.Write(new C().M(ref i));
}
}
static class E
{
public static string M(this C c, ref int i) => "E";
}
En C# 11, la llamada se vincula con E.M
, por lo que se imprime "E"
.
En C# 12, puede vincularse C.M
(con un aviso) y no se busca ningún ámbito de extensión, ya que existe un candidato aplicable, dando lugar a que se imprima "C"
.
También hay un cambio importante en el código fuente debido al mismo motivo.
En el siguiente ejemplo se imprime "1"
en C# 11, pero en C# 12 no se compila debido a un error de ambigüedad.
var i = 5;
System.Console.Write(C.M(null, ref i));
interface I1 { }
interface I2 { }
static class C
{
public static string M(I1 o, ref int x) => "1";
public static string M(I2 o, in int x) => "2";
}
En los ejemplos anteriores se muestran las interrupciones en las invocaciones de métodos, pero dado que son causadas por cambios en la resolución de sobrecarga, también pueden provocarse de forma similar en las conversiones de métodos.
Alternativas
Declaraciones de parámetros
Los autores de API podrían anotar parámetros in
destinados a aceptar solo lvalues con un atributo personalizado y facilitar un analizador para avisar de usos incorrectos.
Esto no permitiría a los autores de API cambiar las firmas de las API existentes que optaron por usar parámetros ref
para no permitir rvalues.
Los autores de llamadas de estas API también necesitarían poner más esfuerzo en obtener un ref
si solo tienen acceso a una variable ref readonly
.
Si se cambian estas API de ref
por [RequiresLocation] in
supondría una cambio importante del código fuente (y, en el caso de los métodos virtuales, también una interrupción del código binario).
En lugar de permitir el modificador ref readonly
, el compilador podría reconocer cuándo se aplica un atributo especial (como [RequiresLocation]
) a un parámetro.
De esto se habló en LDM 2022-04-25, a partir del cual se decidió que se trata de una característica del lenguaje, no un analizador, por lo que debería parecerse a una.
Comprobaciones de tipo de valor
Se permitía pasar lvalues sin modificadores a los parámetros ref readonly
sin advertencias, de igual forma que pasa con los parámetros byref implícitos de C++.
Esto se explicó en LDM 2022-05-11, teniendo en cuenta que la motivación principal para los parámetros ref readonly
es que las APIs capturan o devuelven referencias de estos parámetros, por lo que algún tipo de marcador es útil.
Pasar rvalue a un ref readonly
podría ser un error, no una advertencia.
Eso se aceptó inicialmente en LDM 25-04-2022, pero las discusiones posteriores por correo electrónico lo relajaron porque perderíamos la capacidad de cambiar las API existentes sin interrumpir a los usuarios.
in
podría ser el modificador "natural" del punto de llamada para los parámetros ref readonly
, y usar ref
podría dar como resultado advertencias.
Esto garantizaría un estilo de código coherente y hacer que sea obvio en el elemento callsite que la referencia es de solo lectura (a diferencia de ref
).
Esto se aceptó en un primer momento en LDM 2022-04-25.
Sin embargo, las advertencias podrían ser un punto de fricción para que los autores de API pasen de ref
a ref readonly
.
Además, se ha redefinido in
como ref readonly
y otras funciones prácticas, por lo que esto se rechazó en LDM 2022-05-11.
Revisión de LDM pendiente
Ninguna de las siguientes opciones se implementó en C# 12. Siguen como potenciales propuestas.
Declaraciones de parámetros
Se puede permitir la ordenación inversa de modificadores (readonly ref
en lugar de ref readonly
).
Esto sería incoherente con la forma en que se devuelve readonly ref
y se comportan los campos (la ordenación inversa no se permite o significa algo diferente, respectivamente) y podría entrar en conflicto con parámetros de solo lectura si se implementan en el futuro.
Los valores predeterminados de los parámetros podrían ser un error para los parámetros ref readonly
.
Comprobaciones de tipo de valor
Se podían emitir errores en lugar de advertencias al pasar rvalues a parámetros ref readonly
o al surgir discrepancias entre las anotaciones de sitio de llamada y los modificadores de parámetros.
Del mismo modo, se podrían usar modreq
especiales en lugar de un atributo para asegurarse de que los parámetros de ref readonly
sean distintos de los parámetros de in
en el nivel binario.
Esto proporcionaría garantías más sólidas, por lo que sería bueno para las nuevas API, pero impediría su adopción en las API en tiempo de ejecución existentes, que no pueden introducir cambios importantes.
Las comprobaciones de tipo de valor podían relajarse para permitir pasar referencias de solo lectura a través de ref
a parámetros in
/ref readonly
.
Esto sería similar a cómo funcionan las asignaciones de referencia y las devoluciones de referencia hoy en día; también permiten pasar referencias como de solo lectura a través del modificador ref
en la expresión de origen.
Sin embargo, el ref
normalmente está cerca del lugar donde el destino se declara como ref readonly
, por lo que es evidente que estamos pasando una referencia como de solo lectura, a diferencia de las invocaciones cuyo argumento y modificadores de parámetros suelen estar muy separados.
Además, permiten solo el modificador de ref
, a diferencia de los argumentos, que también permiten in
. De este modo, in
y ref
se volverían intercambiables en los argumentos, o bien in
quedaría prácticamente obsoleto si los usuarios quisieran hacer su código coherente (probablemente usarían ref
en todas partes, ya que es el único modificador permitido para las asignaciones de ref y devoluciones por ref).
Resolución de sobrecarga
La resolución de sobrecarga, la anulación y la conversión podían prohibir la intercambiabilidad de los modificadores ref readonly
y in
.
Podría adoptarse incondicionalmente el cambio de la resolución de sobrecarga en los parámetros in
existentes (sin tener en cuenta la versión del lenguaje), pero eso sería un cambio importante.
Invocar un método de extensión con ref readonly
como receptor podría dar lugar a la advertencia "Se debe pasar el argumento 1 con la palabra clave ref
o in
", como ocurriría en invocaciones que no son de extensión sin modificadores del sitio de llamada (el usuario podría corregir dicha advertencia convirtiendo la invocación del método de extensión en la invocación de un método estático).
Podría aparecer la misma advertencia al usar el inicializador de colección personalizado o el controlador de cadenas interpoladas con el parámetro ref readonly
, aunque el usuario no puede solucionarlo.
Podrían priorizarse las sobrecargas de ref readonly
frente a sobrecargas by-value cuando no hay ningún modificador de sitio de llamada o podría producirse un error de ambigüedad.
Conversiones de métodos
Podríamos permitir que el parámetro ref
del método de destino coincida con el parámetro in
y ref readonly
del delegado.
Esto permitiría a los autores de API cambiar, por ejemplo, ref
a in
en firmas de delegado sin interrumpir a sus usuarios (de forma coherente con lo que se permite para firmas de método normales).
Sin embargo, también daría lugar a la siguiente vulneración de las garantías de readonly
con solo una advertencia:
class Program
{
static readonly int f = 123;
static void Main()
{
var d = (in int x) => { };
d = (ref int x) => { x = 42; }; // warning: mismatch between `ref` and `in`
d(f); // changes value of `f` even though it is `readonly`!
System.Console.WriteLine(f); // prints 42
}
}
Las conversiones de punteros de función podrían indicar una discrepancia de ref readonly
/ref
/in
, pero si quisiéramos validarlo en LangVersion, se necesitaría una inversión importante en la implementación, ya que las conversiones actuales de tipos no necesitan acceso a la compilación.
Además, aunque la discrepancia es actualmente un error, es fácil que los usuarios incorporen una conversión para permitir la discrepancia si así lo desean.
Codificación de metadatos
Se podría permitir especificar RequiresLocationAttribute
en el origen, de forma similar a los atributos In
y Out
.
Como alternativa, podría ser un error cuando se aplica en otros contextos además de solo parámetros, de manera similar al atributo IsReadOnly
, para preservar más espacio de diseño.
Los parámetros ref readonly
de punteros de función podían emitirse con diferentes combinaciones de modopt
/modreq
(tenga en cuenta que la "interrupción de código fuente" en esta tabla hacer referencia a los autores de llamadas con LangVersion <= 11
):
Modificadores | Se puede reconocer en las compilaciones | Los compiladores anteriores los ven como | ref → ref readonly |
in → ref readonly |
---|---|---|---|---|
modreq(In) modopt(RequiresLocation) |
sí | in |
interrupción de código binario y fuente | cambio de código binario |
modreq(In) |
no | in |
interrupción de código binario y fuente | Aceptar |
modreq(RequiresLocation) |
sí | no admitido | interrupción de código binario y fuente | interrupción de código binario y fuente |
modopt(RequiresLocation) |
sí | ref |
cambio de código binario | interrupción de código binario y fuente |
Podríamos emitir atributos [RequiresLocation]
y [IsReadOnly]
para parámetros ref readonly
.
A continuación, in
→ ref readonly
no sería un cambio que rompe la compatibilidad incluso para versiones anteriores del compilador, pero ref
→ ref readonly
se convertiría en un cambio que rompe la compatibilidad del código fuente para las versiones anteriores del compilador (ya que interpretarían ref readonly
como in
, lo que impediría los modificadores ref
) y para nuevas versiones del compilador con LangVersion <= 11
(para mantener la coherencia).
Podríamos hacer que el comportamiento de LangVersion <= 11
sea diferente del comportamiento de las versiones anteriores del compilador.
Por ejemplo, podría ser un error cada vez que se llama a un parámetro ref readonly
(incluso cuando se usa el modificador ref
en el sitio de llamada) o podría permitirse siempre sin errores.
Cambios importantes
Con esta propuesta se sugiere aceptar un cambio importante en el sistema porque no suele tener impacto, está controlado por LangVersion y los usuarios pueden solucionarlo llamando explícitamente al método de la extensión. En su lugar, podríamos mitigarlo mediante
- no permitiendo la discrepancia de
ref
/in
(esto solo impediría la migración ain
para las API antiguas que usabanref
porquein
aún no estaba disponible), - modificando las reglas de resolución de sobrecarga para seguir buscando una mejor correspondencia (determinada por las reglas de optimización indicadas a continuación) cuando hay una discrepancia de tipo ref presente en esta propuesta,
- o, como alternativa, seguir con la discrepancia de
ref
frente ain
, y no para los demás (ref readonly
frente aref
/in
/by-value).
- o, como alternativa, seguir con la discrepancia de
Reglas de optimización
En este ejemplo se producen actualmente tres errores de ambigüedad para las tres invocaciones de M
.
Podríamos agregar nuevas reglas de mejora para resolver las ambigüedades.
Esto también resolvería el cambio importante del código fuente descrito anteriormente.
Una manera sería hacer que el ejemplo imprima 221
(donde el parámetro ref readonly
coincida con el argumento in
, ya que sería una advertencia llamarlo sin modificador, mientras que para el parámetro in
eso está permitido).
interface I1 { }
interface I2 { }
class C
{
static string M(I1 o, in int i) => "1";
static string M(I2 o, ref readonly int i) => "2";
static void Main()
{
int i = 5;
System.Console.Write(M(null, ref i));
System.Console.Write(M(null, in i));
System.Console.Write(M(null, i));
}
}
Las nuevas reglas de mejora podrían marcar como peor el parámetro cuyo argumento podría haberse pasado con un modificador de argumento diferente para mejorarlo.
Es decir, el usuario siempre debe ser capaz de convertir un parámetro peor en un parámetro mejor cambiando su modificador de argumento correspondiente.
Por ejemplo, cuando in
pasa un argumento, es preferible un parámetro ref readonly
en vez de un parámetro in
, porque el usuario podría pasar el argumento by-value para elegir el parámetro in
.
Esta regla es solo una ampliación de la regla de preferencia by-value/in
que se aplica actualmente (es la última regla de resolución de sobrecarga y es mejor la sobrecarga total si cualquiera de los parámetros es mejor y ninguno es peor que el parámetro correspondiente de cualquier otra sobrecarga).
Argumento | mejor parámetro | peor parámetro |
---|---|---|
ref /in |
ref readonly |
in |
ref |
ref |
ref readonly /in |
by-value | by-value/in |
ref readonly |
in |
in |
ref |
Debemos manejar las conversiones de métodos de forma similar.
En el ejemplo siguiente se producen actualmente dos errores de ambigüedad para las dos asignaciones de delegados.
Las nuevas reglas de optimización podrían preferir un parámetro de método cuyo modificador refness coincida con el del parámetro delegado correspondiente en lugar de uno que tenga un desajuste.
Por lo tanto, el ejemplo siguiente imprimiría 12
.
class C
{
void M(I1 o, ref readonly int x) => System.Console.Write("1");
void M(I2 o, ref int x) => System.Console.Write("2");
void Run()
{
D1 m1 = this.M;
D2 m2 = this.M; // currently ambiguous
var i = 5;
m1(null, in i);
m2(null, ref i);
}
static void Main() => new C().Run();
}
interface I1 { }
interface I2 { }
class X : I1, I2 { }
delegate void D1(X s, ref readonly int x);
delegate void D2(X s, ref int x);
Reuniones de diseño
- LDM 25-04-2022: característica aceptada
- LDM 09-05-2022: discusión dividida en tres partes
- LDM 2022-05-11: se permite
ref
y no hay anotación del sitio de llamada en cuanto a los parámetrosref readonly
C# feature specifications