Entonces, (creo que) entiendo lo que hace el in
modificador de parámetro. Pero lo que hace parece ser bastante redundante.
Por lo general, creo que la única razón para usar un ref
sería modificar la variable de llamada, que está explícitamente prohibida por in
. Entonces, pasar por in
referencia parece lógicamente equivalente a pasar por valor.
¿Existe algún tipo de ventaja en el rendimiento? Yo creía que en el lado del back-end de las cosas, un ref
parámetro debe al menos copiar la dirección física de la variable, que debe ser del mismo tamaño que cualquier referencia de objeto típica.
Entonces, ¿la ventaja es solo en estructuras más grandes, o hay alguna optimización del compilador detrás de escena que lo hace atractivo en otros lugares? Si es lo último, ¿por qué no debería hacer que cada parámetro sea an in
?
ref
se utiliza para pasar estructuras por referencia en lugar de copiarlas.in
significa que la estructura no debe modificarse.It means that you should never pass a non-readonly struct as in parameter.
Respuestas:
in
se introdujo recientemente en el lenguaje C #.in
es en realidad unref readonly
. En términos generales, solo hay un caso de uso en el quein
puede ser útil: las aplicaciones de alto rendimiento que se ocupan de muchos correos electrónicos grandesreadonly struct
.Asumiendo que tienes:
readonly struct VeryLarge { public readonly long Value1; public readonly long Value2; public long Compute() { } // etc }
y
void Process(in VeryLarge value) { }
En ese caso, la
VeryLarge
estructura se pasará por referencia sin crear copias defensivas cuando se utilice esta estructura en elProcess
método (por ejemplo, al llamarvalue.Compute()
), y el compilador asegura la inmutabilidad de la estructura.Tenga en cuenta que pasar un not-readonly
struct
con unin
modificador hará que el compilador cree una copia defensiva cuando llame a los métodos de struct y acceda a las propiedades en elProcess
método anterior, lo que afectará negativamente el rendimiento.Hay una entrada de blog de MSDN realmente buena que recomiendo leer con atención.
Si desea obtener más antecedentes históricos de la
in
introducción, puede leer esta discusión en el repositorio de GitHub del lenguaje C #.En general, la mayoría de los desarrolladores están de acuerdo en que la introducción de
in
podría verse como un error. Es una característica de lenguaje bastante exótica y solo puede ser útil en casos de borde de alto rendimiento.fuente
ref
, por ejemplo, permitiendosomeProc(in thing.someProperty);
versuspropType myProp = thing.someProperty; someProc(ref myProp);
? ¿Qué esin
un concepto solo de C #out
o se ha agregado a .NET Framework?in
, porquein
efectivamenteref
tiene un atributo especial. Por lo tanto, su primer fragmento no se compilará. @VisualMelon, es cierto, la copia defensiva se produce al llamar a métodos o al acceder a las propiedades de la estructura desde dentro del método que obtiene la estructura como argumento.in
yout
son conceptos que deberían haber estado en el Framework desde el principio (junto con un medio por el cual los métodos y propiedades podrían indicar si se modificanthis
). Un compilador podría permitir que se pase una propiedad a unin
argumento pasando una referencia a un contenido temporal; si una función escrita en otro idioma modifica ese temporal, la semántica sería un poco asquerosa, pero eso sería culpa de que el comportamiento de la función no coincida con su firma.Correcto.
Si.
No existe el requisito de que una referencia a un objeto y una referencia a una variable sean del mismo tamaño, y no existe el requisito de que ninguna sea del tamaño de una palabra de máquina, pero sí, en la práctica ambas son de 32 bits en 32 máquinas de bits y 64 bits en máquinas de 64 bits.
No tengo claro qué crees que tiene que ver la "dirección física" con esto. En Windows usamos direcciones virtuales , no direcciones físicas en el código de modo de usuario. Tengo curiosidad por saber en qué posibles circunstancias imagina que una dirección física es significativa en un programa de C #.
Tampoco es un requisito que se implemente una referencia de ningún tipo como la dirección virtual del almacenamiento. Las referencias pueden ser identificadores opacos en tablas GC en una implementación conforme a la especificación CLI.
Disminuir el costo de pasar estructuras más grandes es el escenario motivador para la función.
Tenga en cuenta que no hay garantía de que
in
un programa sea realmente más rápido y puede hacer que los programas sean más lentos. Todas las preguntas sobre desempeño deben ser respondidas mediante investigación empírica . Hay muy pocas optimizaciones que siempre sean ganadoras ; esta no es una optimización de "siempre ganar".El compilador y el tiempo de ejecución pueden realizar cualquier optimización que elijan si hacerlo no infringe las reglas de la especificación C #. Que yo sepa, todavía no existe tal optimización para los
in
parámetros, pero eso no excluye tales optimizaciones en el futuro.Bueno, suponga que crea un
int
parámetro en lugar de unin int
parámetro. ¿Qué costos se imponen?Suponga que es un
double
y lo cambia a unin double
. Nuevamente, ahora la variable no se puede registrar en un registro de punto flotante de alto rendimiento. Esto no solo tiene implicaciones en el rendimiento , ¡también puede cambiar el comportamiento del programa! A C # se le permite hacer aritmética flotante con una precisión superior a 64 bits y, por lo general, solo lo hace si los flotantes se pueden registrar.Esta no es una optimización gratuita. Tienes que medir su desempeño contra las alternativas. Su mejor opción es simplemente no hacer estructuras grandes en primer lugar, como sugieren las pautas de diseño.
fuente
in
realmente pasar por pasar por valor en todos los casos? Si tenemosf(ref T x, in T y)
yf
modificax
, debería observar el mismo cambioy
cuando se invoca comof(ref a,a)
. Lo mismo también se aplica sif
toma unin T y
y un delegado que se modificaráy
cuando se llame. Sinin
, la semántica sería diferente, yay
que nunca habría cambiado su valor, creo, ya que sería una copia.in
es representar la noción de "pasar por referencia, solo lectura", que es lógicamente equivalente a pasar por valor cuando te comportas con sensatez .Ahi esta. Al pasar a
struct
, lain
palabra clave permite una optimización donde el compilador solo necesita pasar un puntero, sin el riesgo de que el método cambie el contenido. El último es crítico: evita una operación de copia. En estructuras grandes, esto puede marcar una gran diferencia.fuente
Esto se hace debido al enfoque de programación funcional. Uno de los principales principios es que la función no debe tener efectos secundarios, lo que significa que no debe cambiar los valores de los parámetros y debe devolver algún valor. En C # no había forma de pasar estructuras (y tipo de valor) sin ser copiadas solo por referencia, lo que permite cambiar el valor. En swift hay un algoritmo hacky que copia la estructura (sus colecciones son estructuras por cierto) siempre que el método comience a cambiar sus valores. Las personas que usan Swift no son todas conscientes del material de copia. Esta es una buena característica de C # ya que es eficiente en memoria y explícita. Si miras lo nuevoverá que se hacen más y más cosas alrededor de estructuras y matrices en la pila. Y en la declaración solo es necesario para estas características. Hay limitaciones mencionadas en las otras respuestas, pero no es tan esencial para comprender hacia dónde se dirige .net.
fuente
en él es referencia de solo lectura en c # 7.2
Esto significa que no pasan todo el objeto de pila función similar a la ref caso se pasa única referencia a la estructura
pero el intento de cambiar el valor del objeto produce un error del compilador.
Y sí, esto le permitirá optimizar el rendimiento del código si usa grandes estructuras.
fuente