Я ищу 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.
}
c#
constants
readonly
parameter-passing
Ник Хайнер
источник
источник
Ответы:
К сожалению, на C # это невозможно.
const
Ключевое слово может быть использовано только для локальных переменных и полей.readonly
Ключевое слово может быть использовано только на полях.из http://www.25hoursaday.com/CsharpVsJava.html
РЕДАКТИРОВАТЬ (2019/08/13): я добавляю это для наглядности, так как это принято и занимает первое место в списке. Теперь это возможно с
in
параметрами. См. Ответ под этим для подробностей.источник
Теперь это возможно в 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
означает:источник
in
ключевого слова для параметров с этими типами предотвращает только присвоение им. Не препятствуйте изменению доступных полей или свойств! Я прав?Вот короткий и приятный ответ, который, вероятно, получит много голосов против. Я не прочитал все сообщения и комментарии, поэтому, пожалуйста, простите меня, если это было предложено ранее.
Почему бы не взять ваши параметры и не передать их объекту, который представляет их как неизменяемые, а затем использовать этот объект в своем методе?
Я понимаю, что это, вероятно, очень очевидный обходной путь, который уже рассматривался, и OP пытается избежать этого, задавая этот вопрос, но я чувствовал, что он, тем не менее, должен быть здесь ...
Удачи :-)
источник
int* p; *p = 0;
. Это будет компилироваться и работать до ошибки сегментации.Ответ: C # не имеет функций const, как C ++.
Я согласен с Беннетом Диллом.
Ключевое слово const очень полезно. В этом примере вы использовали int, и люди не поняли вашей точки зрения. Но почему, если ваш параметр - это огромный и сложный объект пользователя, который нельзя изменить внутри этой функции? Это использование ключевого слова const: параметр не может измениться внутри этого метода, потому что [по какой-либо причине] это не имеет значения для этого метода. Ключевое слово Const очень мощное, и мне его очень не хватает в C #.
источник
Начну с
int
порции.int
- это тип значения, а в .Net это означает, что вы действительно имеете дело с копией. Это действительно странное ограничение дизайна - сказать методу: «У вас может быть копия этого значения. Это ваша копия, а не моя; я никогда ее больше не увижу. Но вы не можете изменить копию». В вызове метода неявно подразумевается, что копирование этого значения допустимо, иначе мы не могли бы безопасно вызвать метод. Если для метода требуется оригинал, оставьте его исполнителю сделать копию для сохранения. Либо укажите значение метода, либо не укажите значение. Не делайте перерывов между делом.Перейдем к ссылочным типам. Теперь это немного сбивает с толку. Вы имеете в виду постоянную ссылку, где сама ссылка не может быть изменена, или полностью заблокированный неизменяемый объект? В первом случае ссылки в .Net по умолчанию передаются по значению. То есть вы получаете копию справки. Таким образом, мы имеем практически такую же ситуацию, что и для типов значений. Если разработчику понадобится исходная ссылка, он может оставить ее себе.
Это просто оставляет нам постоянный (заблокированный / неизменяемый) объект. Это может показаться нормальным с точки зрения времени выполнения, но как компилятор может обеспечить это? Поскольку свойства и методы могут иметь побочные эффекты, вы по существу будете ограничены доступом к полю только для чтения. Такой объект вряд ли будет очень интересным.
источник
Создайте интерфейс для своего класса, который имеет только средства доступа к свойствам только для чтения. Тогда пусть ваш параметр относится к этому интерфейсу, а не к самому классу. Пример:
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» перед именем идентификатора.
Всем тем, кто хочет сейчас нажать кнопку «против», пожалуйста, прочтите мнения этих светил по этой теме:
источник
Я знаю, что это может быть немного поздно. Но для людей, которые все еще ищут другие способы решения этой проблемы, может быть другой способ обойти это ограничение стандарта C #. Мы могли бы написать класс-оболочку ReadOnly <T> где T: struct. С неявным преобразованием в базовый тип T. Но только явное преобразование в класс-оболочку <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; } }
источник
Если структура передается в метод, если она не передана по ссылке, она не будет изменена методом, в который она передана. Так что в этом смысле да.
Можете ли вы создать параметр, значение которого не может быть присвоено в методе или свойства которого не могут быть установлены в рамках метода? Нет. Вы не можете предотвратить присвоение значения в методе, но вы можете предотвратить установку его свойств, создав неизменный тип.
Вопрос не в том, можно ли назначить параметр или его свойства внутри метода. Вопрос в том, что будет после выхода из метода.
Единственный раз, когда какие-либо внешние данные будут изменены, - это если вы передадите класс и измените одно из его свойств или если вы передадите значение с помощью ключевого слова ref. Ситуация, которую вы обрисовали, не делает ни того, ни другого.
источник
x++
. То есть он пытается предотвратить переназначение параметра, которое ортогонально изменчивости значения параметра.