Могут ли параметры быть постоянными? - PullRequest
78 голосов
/ 26 февраля 2010

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

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

public Foo(final int bar);

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

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

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 ]

59 голосов
/ 26 февраля 2010

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

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

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

ПРИМЕЧАНИЕ. Язык Java также поддерживает наличие окончательных параметров метода. Эта функциональность отсутствует в C #.

из http://www.25hoursaday.com/CsharpVsJava.html

20 голосов
/ 03 января 2018

Теперь это возможно в 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 означает:

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

8 голосов
/ 20 декабря 2011

Ответ: C # не имеет такой же функциональности, как C ++.

Я согласен с Беннеттом Диллом.

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

8 голосов
/ 26 февраля 2010

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

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

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

7 голосов
/ 26 февраля 2010

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

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

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

Удачи: -)

4 голосов
/ 24 августа 2013

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

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 и других подобных правил.

3 голосов
/ 26 февраля 2010

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

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

2 голосов
/ 30 апреля 2016

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

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

ИСПОЛЬЗОВАНИЕ 2, которое я бы предложил, имеет использование Debug vs Release, продемонстрированное в функции 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;
  }

}
0 голосов
/ 26 февраля 2010

Если структура передается в метод, если она не передана с помощью ref, она не будет изменена методом, в который она передана.Так что в этом смысле да.

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

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

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

...