我正在寻找Java的final
的C#等价物。它存在吗?
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#7.2版中实现:
您可以在方法签名中使用in
关键字。 MSDN documentation。
在指定方法的参数之前,应添加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
标记一个参数意味着:
此方法不会修改用作此参数的参数的值。
我将从int
部分开始。 int
是一种值类型,在.Net中意味着你真的在处理一个副本。这是一个非常奇怪的设计约束,告诉方法“你可以得到这个值的副本。这是你的副本,而不是我的副本;我永远不会再看到它了。但你无法改变副本。”在方法调用中隐含了复制此值是可以的,否则我们无法安全地调用该方法。如果该方法需要原始文件,请将其留给实施者进行复制以保存原文。要么给方法赋值,要么不给方法赋值。不要在两者之间全力以赴。
让我们继续讨论类型。现在它有点令人困惑。你的意思是一个恒定的引用,其中引用本身不能被更改,或者一个完全锁定,不可更改的对象?如果是前者,默认情况下.Net中的引用是按值传递的。也就是说,您获得了参考文献的副本。因此,我们与价值类型的情况基本相同。如果实现者需要原始引用,他们可以自己保留它。
这只是给我们留下了常量(锁定/不可变)对象。从运行时的角度来看,这似乎没什么问题,但编译器如何强制执行呢?由于属性和方法都有副作用,因此基本上只限于只读字段访问。这样的对象不太可能非常有趣。
答案:C#没有像C ++那样的const功能。
我同意Bennett Dill的观点。
const关键字非常有用。在这个例子中,你使用了一个int,人们不明白你的观点。但是,为什么如果你的参数是一个用户庞大而复杂的对象,在该函数内部无法更改?这是使用const关键字:参数不能在该方法内部进行更改,因为[此处无论什么原因]对该方法无关紧要。 Const关键字非常强大,我真的很想念它在C#中。
这是一个简短而又甜蜜的答案,可能会得到很多票数。我没有阅读所有的帖子和评论,所以请原谅我,如果以前曾建议的那样。
为什么不把你的参数传递给一个将它们公开为不可变的对象,然后在你的方法中使用该对象?
我意识到这可能是一个非常明显的工作,已经考虑过,OP试图通过提出这个问题来避免这样做,但我觉得它应该在这里,不过不...
祝好运 :-)
为只具有只读属性访问器的类创建一个接口。然后让你的参数是该接口而不是类本身。例:
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和其他此类规则。
如果你经常遇到这样的麻烦,那么你应该考虑“应用匈牙利”。好的,与bad kind相反。虽然这通常不会尝试表达方法参数的常量(这太不寻常),但肯定没有什么可以阻止您在标识符名称之前添加额外的“c”。
对于所有那些渴望抨击downvote按钮的人,请阅读这些主题关于这个主题的意见:
我知道这可能有点晚了。但对于那些仍在寻找其他方式的人来说,可能还有另一种方法可以解决这种C#标准的限制问题。我们可以编写包装类ReadOnly <T>,其中T:struct。使用隐式转换为基类型T.但只能显式转换为包装器<T>类。如果开发人员尝试将隐式设置为ReadOnly <T>类型的值,则会强制执行编译器错误。我将在下面展示两种可能的用途。
USAGE 1需要更改调用者定义。此用法仅用于测试“TestCalled”功能代码的正确性。在发布级别/版本中,您不应该使用它。由于大规模的数学运算可能会导致转换过度,并使代码变慢。我不会使用它,但出于演示目的,我只发布了它。
我建议使用USAGE 2,在TestCalled2函数中演示了Debug vs Release使用。使用这种方法时,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关键字传递值。你概述的情况都没有。