Estoy buscando el equivalente en C # de Java final
. ¿Existe?
¿C # tiene algo parecido a lo siguiente:
public Foo(final int bar);
En el ejemplo anterior, bar
es una variable de solo lectura y no se puede cambiar por Foo()
. ¿Hay alguna forma de hacer esto en C #?
Por ejemplo, tal vez tengo un método mucho tiempo que va a trabajar con x
, y
y z
las coordenadas de un objeto (enteros). Quiero estar absolutamente seguro de que la función no altera estos valores de ninguna manera, corrompiendo así los datos. Por lo tanto, me gustaría declararlos de solo lectura.
public Foo(int x, int y, int z) {
// do stuff
x++; // oops. This corrupts the data. Can this be caught at compile time?
// do more stuff, assuming x is still the original value.
}
c#
constants
readonly
parameter-passing
Nick Heiner
fuente
fuente
Respuestas:
Desafortunadamente, no puede hacer esto en C #.
La
const
palabra clave solo se puede utilizar para campos y variables locales.La
readonly
palabra clave solo se puede utilizar en campos.desde http://www.25hoursaday.com/CsharpVsJava.html
EDITAR (2019/08/13): Estoy lanzando esto para mayor visibilidad, ya que se acepta y es el más alto de la lista. Ahora es posible con
in
parámetros. Vea la respuesta debajo de esta para más detalles.fuente
Esto ahora es posible en C # versión 7.2:
Puede utilizar la
in
palabra clave en la firma del método. Documentación de MSDN .los
in
palabra clave debe agregarse antes de especificar el argumento de un método.Ejemplo, un método válido en C # 7.2:
public long Add(in long x, in long y) { return x + y; }
Si bien lo siguiente no está permitido:
public long Add(in long x, in long y) { x = 10; // It is not allowed to modify an in-argument. return x + y; }
Se mostrará el siguiente error al intentar modificar cualquiera
x
oy
ya que están marcados conin
:Marcar un argumento con
in
medios:fuente
in
palabras clave para parámetros con esos tipos, solo evita asignarles. ¡No evite modificar sus campos o propiedades accesibles! Estoy en lo cierto?Aquí hay una respuesta corta y dulce que probablemente obtendrá muchos votos negativos. No he leído todas las publicaciones y comentarios, así que perdóneme si esto ha sido sugerido anteriormente.
¿Por qué no tomar sus parámetros y pasarlos a un objeto que los expone como inmutables y luego usar ese objeto en su método?
Me doy cuenta de que esta es probablemente una solución muy obvia que ya se ha considerado y el OP está tratando de evitar hacer esto haciendo esta pregunta, pero sentí que debería estar aquí de todos modos ...
Buena suerte :-)
fuente
int* p; *p = 0;
. Eso se compilará y se ejecutará hasta la falla de segmentación.La respuesta: C # no tiene la funcionalidad constante como C ++.
Estoy de acuerdo con Bennett Dill.
La palabra clave const es muy útil. En el ejemplo, usaste un int y la gente no entiende tu punto. Pero, ¿por qué si su parámetro es un objeto enorme y complejo de usuario que no se puede cambiar dentro de esa función? Ese es el uso de la palabra clave const: el parámetro no puede cambiar dentro de ese método porque [cualquiera sea la razón aquí] eso no importa para ese método. La palabra clave Const es muy poderosa y realmente la extraño en C #.
fuente
Empezaré por la
int
porción.int
es un tipo de valor, y en .Net eso significa que realmente se trata de una copia. Es una restricción de diseño realmente extraña decirle a un método "Puedes tener una copia de este valor. Es tu copia, no la mía; nunca la volveré a ver. Pero no puedes cambiar la copia". Está implícito en la llamada al método que copiar este valor está bien, de lo contrario no podríamos haber llamado al método de forma segura. Si el método necesita el original, deje que el implementador haga una copia para guardarlo. O dé el valor al método o no dé el valor al método. No te vuelvas loco en el medio.Pasemos a los tipos de referencia. Ahora se vuelve un poco confuso. ¿Te refieres a una referencia constante, donde la referencia en sí no se puede cambiar, o un objeto completamente bloqueado e inmutable? Si es el primero, las referencias en .Net por defecto se pasan por valor. Es decir, obtienes una copia de la referencia. Entonces, tenemos esencialmente la misma situación que para los tipos de valor. Si el implementador necesita la referencia original, puede conservarla él mismo.
Eso solo nos deja con un objeto constante (bloqueado / inmutable). Esto puede parecer correcto desde la perspectiva del tiempo de ejecución, pero ¿cómo va a hacer cumplir el compilador? Dado que las propiedades y los métodos pueden tener efectos secundarios, esencialmente estaría limitado al acceso de campo de solo lectura. No es probable que un objeto así sea muy interesante.
fuente
Cree una interfaz para su clase que solo tenga accesores de propiedad de solo lectura. Luego, haga que su parámetro sea de esa interfaz en lugar de la clase en sí. Ejemplo:
public interface IExample { int ReadonlyValue { get; } } public class Example : IExample { public int Value { get; set; } public int ReadonlyValue { get { return this.Value; } } } public void Foo(IExample example) { // Now only has access to the get accessors for the properties }
Para estructuras, cree un contenedor const genérico.
public struct Const<T> { public T Value { get; private set; } public Const(T value) { this.Value = value; } } public Foo(Const<float> X, Const<float> Y, Const<float> Z) { // Can only read these values }
Sin embargo, vale la pena señalar que es extraño que quieras hacer lo que estás pidiendo con respecto a las estructuras, como autor del método, debes esperar saber qué está sucediendo en ese método. No afectará los valores pasados para modificarlos dentro del método, por lo que su única preocupación es asegurarse de comportarse en el método que está escribiendo. Llega un punto en el que la vigilancia y el código limpio son la clave, por encima de la aplicación de reglas y otras reglas similares.
fuente
Si a menudo tiene problemas como este, entonces debería considerar "aplicaciones húngaras". La buena, en contraposición a la mala . Si bien esto normalmente no intenta expresar la constancia de un parámetro de método (eso es demasiado inusual), ciertamente no hay nada que le impida agregar una "c" adicional antes del nombre del identificador.
Para todos aquellos que ansían presionar el botón de voto negativo ahora, lean las opiniones de estas luminarias sobre el tema:
fuente
Sé que esto podría ser un poco tarde. Pero para las personas que todavía están buscando otras formas de hacerlo, podría haber otra forma de evitar esta limitación del estándar C #. Podríamos escribir la clase contenedora ReadOnly <T> donde T: struct. Con conversión implícita al tipo base T. Pero solo conversión explícita a la clase contenedora <T>. Lo que hará cumplir los errores del compilador si el desarrollador intenta establecer implícitamente un valor de tipo ReadOnly <T>. Como demostraré a continuación dos posibles usos.
USAGE 1 requería cambiar la definición de la persona que llama. Este uso solo tendrá uso para probar la exactitud de su código de funciones "TestCalled". Mientras esté en el nivel de lanzamiento / compilaciones, no debe usarlo. Dado que en las operaciones matemáticas a gran escala pueden ser excesivas en las conversiones y hacer que su código sea lento. No lo usaría, pero solo lo he publicado para fines de demostración.
USAGE 2 que sugeriría, tiene el uso de Debug vs Release demostrado en la función TestCalled2. Además, no habría conversión en la función TestCaller cuando se usa este enfoque, pero requiere un poco más de codificación de las definiciones de TestCaller2 usando el acondicionamiento del compilador. Puede notar errores del compilador en la configuración de depuración, mientras que en la configuración de lanzamiento todo el código en la función TestCalled2 se compilará correctamente.
using System; using System.Collections.Generic; public class ReadOnly<VT> where VT : struct { private VT value; public ReadOnly(VT value) { this.value = value; } public static implicit operator VT(ReadOnly<VT> rvalue) { return rvalue.value; } public static explicit operator ReadOnly<VT>(VT rvalue) { return new ReadOnly<VT>(rvalue); } } public static class TestFunctionArguments { static void TestCall() { long a = 0; // CALL USAGE 1. // explicite cast must exist in call to this function // and clearly states it will be readonly inside TestCalled function. TestCalled(a); // invalid call, we must explicit cast to ReadOnly<T> TestCalled((ReadOnly<long>)a); // explicit cast to ReadOnly<T> // CALL USAGE 2. // Debug vs Release call has no difference - no compiler errors TestCalled2(a); } // ARG USAGE 1. static void TestCalled(ReadOnly<long> a) { // invalid operations, compiler errors a = 10L; a += 2L; a -= 2L; a *= 2L; a /= 2L; a++; a--; // valid operations long l; l = a + 2; l = a - 2; l = a * 2; l = a / 2; l = a ^ 2; l = a | 2; l = a & 2; l = a << 2; l = a >> 2; l = ~a; } // ARG USAGE 2. #if DEBUG static void TestCalled2(long a2_writable) { ReadOnly<long> a = new ReadOnly<long>(a2_writable); #else static void TestCalled2(long a) { #endif // invalid operations // compiler will have errors in debug configuration // compiler will compile in release a = 10L; a += 2L; a -= 2L; a *= 2L; a /= 2L; a++; a--; // valid operations // compiler will compile in both, debug and release configurations long l; l = a + 2; l = a - 2; l = a * 2; l = a / 2; l = a ^ 2; l = a | 2; l = a & 2; l = a << 2; l = a >> 2; l = ~a; } }
fuente
Si la estructura se pasa a un método, a menos que sea pasada por ref, no será cambiada por el método al que se pasa. Entonces, en ese sentido, sí.
¿Puede crear un parámetro cuyo valor no se pueda asignar dentro del método o cuyas propiedades no se puedan establecer dentro del método? No. No puede evitar que se asigne el valor dentro del método, pero puede evitar que se establezcan sus propiedades creando un tipo inmutable.
La pregunta no es si el parámetro o sus propiedades se pueden asignar dentro del método. La pregunta es qué será cuando salga el método.
La única vez que se modificará cualquier dato externo es si pasa una clase y cambia una de sus propiedades, o si pasa un valor utilizando la palabra clave ref. La situación que ha delineado no lo hace.
fuente
x++
. Es decir, está tratando de evitar la reasignación del parámetro, que es ortogonal a la mutabilidad del valor del parámetro.