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

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

Имеет ли С# что-нибудь вроде следующего:

public Foo(final int bar);

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

Например, возможно, у меня есть длинный метод, который будет работать с координатами 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.
}

Ответ 1

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

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

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

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

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

ОБНОВЛЕНИЕ (2019/08/13): Я добавлю это для наглядности, так как это принято и занимает первое место в списке. Теперь это возможно с параметрами in. Смотрите ответ ниже этого для подробностей.

Ответ 2

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

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

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

Пример, допустимый метод в С# 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 означает:

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

Ответ 3

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

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

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

Ответ 4

Ответ: С# не имеет функции const, такой как С++.

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

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

Ответ 5

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

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

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

Удачи: -)

Ответ 6

Создайте интерфейс для своего класса, который имеет только объекты доступа к 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 и другими такими правилами.

Ответ 7

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

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

Ответ 8

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

ИСПОЛЬЗОВАНИЕ 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;
  }

}

Ответ 9

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

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

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

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