Могут ли параметры быть постоянными?

Я ищу эквивалент C# для Java final. Существует ли она?

есть ли у C# что-то вроде следующего:

public Foo(final int bar);

в приведенном выше примере bar является переменной только для чтения и не может быть изменена на Foo(). Есть ли способ сделать это в C#?

например, возможно, у меня есть длинный метод, который будет работать с x, y и z координаты некоторого объекта (ints). Я хочу быть абсолютно уверен что функция никоим образом не изменяет эти значения, тем самым повреждая данные. Поэтому я хотел бы объявить их только для чтения.

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.
}

9 ответов


к сожалению вы не можете сделать это в C#.

на const ключевое слово может использоваться только для локальных переменных и полей.

на readonly ключевое слово может использоваться только в полях.

Примечание: язык Java также поддерживает наличие конечных параметров метода. Эта функциональность отсутствует в C#.

от http://www.25hoursaday.com/CsharpVsJava.html


Я начну с int часть. int - Это тип значения, а в .Net это означает, что вы действительно имеете дело с копией. Это действительно странное ограничение дизайна, чтобы сказать методу: "у вас может быть копия этого значения. Это ваша копия, не моя, я ее больше никогда не увижу. Но вы не можете изменить копию."В вызове метода подразумевается, что копирование этого значения в порядке, иначе мы не могли бы безопасно вызвать метод. Если метод нуждается в оригинале, оставьте его для реализации сделайте копию, чтобы сохранить его. Либо дайте методу значение, либо не давайте методу значение. Не делай из мухи слона.

давайте перейдем к ссылочным типам. Теперь это становится немного запутанным. Вы имеете в виду постоянную ссылку, где сама ссылка не может быть изменена, или полностью заблокированный, неизменяемый объект? Если первый, ссылки в .Net по умолчанию передаются по значению. То есть вы получите копию справки. Так у нас по сути такая же ситуация, как для типов значений. Если исполнителю понадобится исходная ссылка, они могут сохранить ее сами.

нас остается постоянной (заблокировано/неизменный) объект. Это может показаться нормальным с точки зрения времени выполнения, но как компилятор должен его применять? Поскольку свойства и методы могут иметь побочные эффекты, вы по существу будете ограничены доступом только для чтения. Такой объект вряд ли будет интересен.


вот короткий и сладкий ответ, который, вероятно, получит много голосов. Я не читал все посты и комментарии, поэтому, пожалуйста, простите меня, если это было ранее предложено.

Почему бы не взять ваши параметры и передать их в объект, который представляет их как незыблемые, а затем использовать этот объект в метод?

Я понимаю, что это, вероятно, очень очевидная работа, которая уже рассматривалась, и OP пытается избежать этого, задавая этот вопрос, но я чувствовал, что он должен быть здесь, тем не менее...

удачи :-)


ответ: C# не имеет функций const, таких как c++.

Я согласен с Беннетом укроп.

ключевое слово const очень полезно. В примере вы использовали int, и люди не поняли вашу точку зрения. Но почему, если параметр-это огромный и сложный объект пользователя, который нельзя изменить внутри этой функции? Это использование ключевого слова const: параметр не может измениться внутри этого метода, потому что [по какой-либо причине здесь] это не имеет значения для этого метода. Ключевое слово Const очень мощный, и я действительно скучаю по нему в C#.


теперь это возможно в C# версии 7.2:

можно использовать in ключевое слово в сигнатуре метода. документация MSDN.

на in ключевое слово должно быть добавлено перед указанием аргумента метода.

пример допустимого метода в C# 7.2:

public long Add(in long x, in long y)
{
    return x + y;
}

в то время как следующее Не допускается:

public long Add(in long x, in long y)
{
    x = 10; // It is not allowed to modify an in-argument.
    return x + y;
}

следующая ошибка будет показана при попытке изменить либо x или y так как они с пометкой in:

не может назначить переменной "in long", потому что это переменная только для чтения

маркировка ссоры с in означает:

этот метод не изменяет значение аргумента, используемого в качестве этого параметра.


Если вы часто сталкиваетесь с проблемами, как это, то вы должны рассмотреть "приложения венгерский". Хороший вид, в отличие от плохой вид. Хотя это обычно не пытается выразить константу параметра метода (это слишком необычно), нет ничего, что остановило бы вас от добавления дополнительного "c" перед именем идентификатора.

для всех тех, кто хочет хлопнуть кнопку downvote сейчас, пожалуйста, прочитайте мнения этих светил на тема:


создайте интерфейс для своего класса, который имеет только доступы свойств readonly. Тогда ваш параметр должен быть этого интерфейса, а не самого класса. Пример:

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
}

для структур создайте общую оболочку const.

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
}

его стоит отметить, что странно, что вы хотите сделать то, что вы просите сделать в отношении структур, как автор метода, который вы должны ожидать, чтобы знать, что происходит в этом методе. Это не повлияет на значения передано, чтобы изменить их в методе, поэтому ваша единственная забота - убедиться, что вы ведете себя в методе, который вы пишете. Наступает момент, когда бдительность и чистый код являются ключевыми, а не соблюдение const и других подобных правил.


Я знаю, что это может быть немного поздно. Но для людей, которые все еще ищут другие способы для этого, может быть другой способ обойти это ограничение стандарта C#. Мы могли бы написать класс оболочки ReadOnly где T: struct. С неявным преобразованием в базовый тип T. Но только явное преобразование в класс wrapper. Что приведет к ошибкам компилятора, если разработчик попытается установить неявное значение типа ReadOnly. Ниже я продемонстрирую два возможных применения.

использование 1 требуется определение вызывающего абонента для изменения. Это использование будет использоваться только при тестировании правильности вашего кода функций "TestCalled". В то время как на уровне выпуска/сборки вы не должны использовать его. Поскольку в крупномасштабных математических операциях может быть перебор в преобразованиях и сделать ваш код медленным. Я бы не использовал его, но для демонстрационной цели только я разместил его.

использование 2, которое я бы предложил, имеет отладку против использования выпуска, продемонстрированного в функции TestCalled2. Также не было бы преобразование в функции TestCaller при использовании этого подхода, но это требует немного больше кодирования определений TestCaller2 с использованием кондиционирования компилятора. Вы можете заметить ошибки компилятора в конфигурации отладки, в то время как в конфигурации выпуска весь код в функции TestCalled2 будет успешно скомпилирован.

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;
  }

}

Если struct передается в метод, если он не передан ref, он не будет изменен методом, в который он передан. В этом смысле, да.

можете ли вы создать параметр, значение которого не может быть назначено в методе или свойства которого не могут быть установлены в методе? Нет. Вы не можете запретить присвоение значения в рамках метода, но вы можете запретить его свойства, создав неизменяемый тип.

вопрос не можно ли назначить параметр или его свойства в пределах метода. Вопрос в том, что будет, когда метод завершится.

единственный раз, когда любые внешние данные будут изменены, - это если вы передадите класс и измените одно из его свойств или передадите значение с помощью ключевого слова ref. Ситуацию вы изложили не.