参数可以保持不变?
我正在寻找C#相当于Java的final
。 它存在吗?
C#是否有如下所示的内容:
public Foo(final int bar);
在上面的例子中, bar
是一个只读variables,不能被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#中做到这一点。
const
关键字只能用于局部variables和字段。
readonly
关键字只能用于字段。
注:Java语言也支持对方法的最终参数。 这个function在C#中是不存在的。
这是一个简短而又甜蜜的回答,可能会得到很多的反对票。 我没有阅读所有的post和评论,所以请原谅,如果这是以前的build议。
为什么不把你的参数,并将它们传递到一个对象,暴露他们作为不可变的,然后在你的方法中使用该对象?
我意识到这可能是一个非常明显的解决办法,已经考虑到了,OP正在试图通过提出这个问题来避免这样做,但是我觉得它应该在这里无所谓…
祝你好运 :-)
我将从int
部分开始。 int
是一个值types,在.Net中,这意味着你真的在处理一个副本。 这是一个非常奇怪的devise约束,告诉方法“你可以有一个这个值的副本,这是你的副本,不是我的,我永远不会再看到它,但你不能改变副本。 在方法调用中隐含的是复制这个值是可以的,否则我们不能安全地调用这个方法。 如果方法需要原件,请将其留给实施者进行复制以保存。 要么给方法的价值,要么不给方法的价值。 不要在这两个人之间匆匆而过。
我们来看看引用types。 现在有点混乱。 你的意思是一个常量引用,引用本身是不能改变的,还是一个完全locking的,不可改变的对象? 如果前者,默认情况下.Net中的引用是按值传递的。 也就是说,你得到了一个参考的副本。 所以我们和价值types的情况基本相同。 如果实现者需要原始引用,他们可以自己保留。
这只是给我们留下了常量(locking/不可变)的对象。 从运行时的angular度来看,这看起来没问题,但编译器如何执行呢? 由于属性和方法都可能有副作用,所以本质上只限于只读字段访问。 这样的对象不可能是非常有趣的。
答案是:C#没有像C ++那样的constfunction。
我同意贝内特莳萝。
const关键字非常有用。 在这个例子中,你使用了一个int,人们不明白你的意思。 但是,为什么如果你的参数是一个用户庞大而复杂的对象,不能在该函数内部进行更改呢? 这是使用const关键字:参数不能在该方法内更改,因为[无论这里的任何原因],对于该方法无关紧要。 Const关键字非常强大,我真的很想念C#。
如果你经常遇到这样的麻烦,那么你应该考虑“应用程序匈牙利语”。 好样的,而不是那种坏的样子 。 虽然这通常不会试图表示方法参数的恒定性(这太不寻常了),但肯定没有任何东西阻止你在标识符名称之前附加一个额外的“c”。
对于所有那些渴望猛击downvotebutton,请阅读这些名人对这个话题的意见:
- Eric Lippert
- 拉里奥斯特曼
- Joel Spolsky
为只有只读属性访问器的类创build一个接口。 然后让你的参数是该接口而不是类本身。 例:
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 }
对于结构体,创build一个通用的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>,其中T:struct。 用隐式转换为基typesT,但只显式转换为包装类<T>类。 如果开发人员尝试将隐式设置为ReadOnly <T>types的值,那么这将强制执行编译器错误。 我将在下面演示两种可能的用法。
使用1需要调用者定义更改。 这个用法只能用于testing“TestCalled”函数代码的正确性。 在发布级别/构build时,您不应该使用它。 由于在大规模的math运算可能过度的转换,并使你的代码变慢。 我不会使用它,但仅用于示范目的,我已经发布了它。
我build议的USAGE 2,已经在TestCalled2函数中演示了Debug vs Release的使用。 在使用这种方法时,TestCaller函数中也不会有任何转换,但是需要使用编译器调节对TestCaller2定义进行更多的编码。 您可以在debuggingconfiguration中注意到编译器错误,而在发布configuration中,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传递,它将不会被传入的方法改变。 所以从这个意义上说,是的。
你可以创build一个参数,其值不能在方法内赋值,或者在方法内不能设置其属性? 不可以。您不能阻止在方法中分配值,但可以通过创build不可变types来防止其属性被设置。
问题不在于该参数或其属性是否可以在方法内分配。 问题是什么时候该方法退出。
任何外部数据将被改变的唯一时间是如果你通过一个类并改变它的一个属性,或者如果你通过使用ref关键字传递一个值。 你所概述的情况都没有。