¿Los parámetros pueden ser constantes?


Estoy buscando el equivalente en C# de final de Java. ¿Existe?

Does C# have anything like the following:

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 manera de hacer esto en C#?

Por ejemplo, tal vez tengo un método largo que funcionará con x, y, y z coordenadas de algún objeto (ints). Quiero estar absolutamente seguro de que la función no altera estos valores en de cualquier manera, corrompiendo así los datos. Por lo tanto, me gustaría declararlos solo en 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.
}
Author: John Saunders, 2010-02-26

9 answers

Desafortunadamente no puede hacer esto en C#.

El const la palabra clave solo se puede usar para variables y campos locales.

El readonly la palabra clave solo se puede usar en campos.

NOTA: El lenguaje Java también soporta tener parámetros finales para un método. Esta funcionalidad no existe en C#.

De http://www.25hoursaday.com/CsharpVsJava.html

 55
Author: Corey Sunwold,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-26 10:18:50

Comenzaré con la porción int. int es un tipo de valor, y en.Net eso significa que realmente está tratando con 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 con seguridad al método. Si el método necesita el original, deje que el implementador haga un copiar para guardarlo. Dé al método el valor o no dé al método el valor. No te hagas el caprichoso entre medias.

Pasemos a los tipos de referencia. Ahora se pone un poco confuso. ¿Quiere decir una referencia constante, donde la referencia en sí no se puede cambiar, o un objeto completamente bloqueado e inmutable? Si el primero, las referencias en. Net de forma predeterminada se pasan por valor. Es decir, obtienes una copia de la referencia. Así que tenemos esencialmente la misma situación que para el valor tipo. Si el implementador necesita la referencia original, puede conservarla por sí mismo.

Eso nos deja con un objeto constante (bloqueado/inmutable). Esto puede parecer correcto desde una perspectiva de tiempo de ejecución, pero ¿cómo es que el compilador lo hace cumplir? Dado que las propiedades y los métodos pueden tener efectos secundarios, esencialmente estaría limitado al acceso de campo de solo lectura. Tal objeto no es probable que sea muy interesante.

 8
Author: Joel Coehoorn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-26 02:23:25

Aquí hay una respuesta corta y dulce que probablemente obtendrá muchos votos negativos. No he leído todos los posts y comentarios, así que por favor perdóname 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 este es probablemente un trabajo muy obvio que ya se ha considerado y el OP está tratando de evitar hacer esto haciendo esta pregunta, pero Sentí que debería estar aquí sin embargo...

Buena suerte :-)

 8
Author: Bennett Dill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-26 04:05:42

La respuesta: C# no tiene la funcionalidad const 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 el 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 const keyword: parameter no puede cambiar dentro de ese método porque [sea cual sea la razón aquí] eso no importa para ese método. La palabra clave Const es muy poderosa y realmente lo extraño en C#.

 8
Author: Michel Vaz Ramos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-20 18:09:10

Esto ahora es posible en la versión 7.2 de C#:

Puede usar la palabra clave in en la firma del método. Documentación de MSDN.

La palabra clave in debe añadirse 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;
}

Mientras que 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 x o y ya que están marcados con in:

No se puede asignar a variable 'in long' porque es una variable de solo lectura

Marcar un argumento con in significa:

Este método no modifica el valor del argumento utilizado como este parámetro.

 6
Author: Max,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-02 21:30:56

Si a menudo te encuentras con problemas como este, entonces deberías considerar "aplicaciones húngaras". La buena, a diferencia de la mala . Si bien esto normalmente no intenta expresar la constante de un parámetro de método (eso es demasiado inusual), ciertamente no hay nada que le impida marcar una "c" adicional antes del nombre del identificador.

Para todos aquellos que están ansiosos por golpear el botón de voto descendente ahora, por favor lea las opiniones de estas luminarias sobre el tema:

 4
Author: Hans Passant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-26 03:06:33

Cree una interfaz para su clase que solo tenga accesores de propiedad readonly. Luego haga que su parámetro sea de esa interfaz en lugar de la propia clase. 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 una envoltura const genérica.

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
}

Vale la pena señalar, sin embargo, que es extraño que quieras hacer lo que estás pidiendo hacer con respecto a las estructuras, como el escritor del método que debe esperar saber lo que está pasando en ese método. No afectará a los valores pasados para modificarlos dentro del método, por lo que su única preocupación es asegurarse de que se comporta en el método que está escribiendo. Llega un punto donde la vigilancia y el código limpio son la clave, sobre cumplimiento de const y otras reglas.

 4
Author: Steve Lillis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-24 17:21:39

Sé que esto puede ser un poco tarde. Pero para las personas que todavía están buscando otras formas para esto, podría haber otra forma de evitar esta limitación del estándar C#. Podríamos escribir la clase wrapper ReadOnly donde T: struct. Con conversión implícita al tipo base T. Pero solo conversión explícita a clase wrapper. Que hará cumplir los errores del compilador si el desarrollador intenta establecer un valor implícito de tipo ReadOnly. Como voy a demostrar dos posibles usos a continuación.

USO 1 llamada requerida definición a cambiar. Este uso solo tendrá uso en la prueba de la corrección de su código de funciones" TestCalled". Mientras que en el nivel de lanzamiento / builds no debería usarlo. Dado que en operaciones matemáticas a gran escala puede exagerar en las conversiones, y hacer que su código lento. No lo usaría, pero para fines de demostración solo lo he publicado.

EL USO 2 que sugeriría, tiene el uso de depuración vs Liberación demostrado en la función TestCalled2. También no habría conversión en TestCaller función cuando se utiliza este enfoque, pero requiere un poco más de codificación de definiciones TestCaller2 utilizando el acondicionamiento del compilador. Puede notar errores del compilador en la configuración de depuración, mientras que en la configuración de liberación 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;
  }

}
 2
Author: SoLaR,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-30 14:20:28

Si struct se pasa a un método, a menos que sea pasado por ref, no será cambiado por el método al que se pasa. Así que en ese sentido, sí.

¿Puede crear un parámetro cuyo valor no se puede asignar dentro del método o cuyas propiedades no se pueden establecer mientras esté dentro del método? No. No puede evitar que el valor se asigne dentro del método, pero puede evitar que se establezcan sus propiedades creando un tipo inmutable.

La pregunta no es si el 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 cualquier dato externo va a ser alterado es si pasas una clase y cambias una de sus propiedades, o si pasas un valor usando la palabra clave ref. La situación que has descrito no hace ninguna de las dos cosas.

 0
Author: David Morton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-26 02:45:51