与同事就此事进行友好的辩论。我们对此有一些想法,但想知道这么多人是怎么想的?
- 你有其他语言有这个功能吗?这似乎很明显。
- @ CaleLeaCurrar C和C++具有const局部变量,可以用运行时计算值初始化它们。
- javascript 2015(es6)具有const类型。例如,const mylist=[1,2,3];。使用这个结构是非常好的编程实践。更多信息:developer.mozilla.org/en-us/docs/web/javascript/reference/…
- 对于那些感兴趣的人,有一个用户语音建议这个功能。它目前只坐在87票,所以如果你想看到本地只读变量,请去撞它!
- 这不仅仅是语言问题。在包括最高级别的首席专家在内的社区中,大多数人都存在这样一个问题:他们不关心常量的正确性以及与之相关的任何内容。抵抗是徒劳的。
- 更新2017:请投票赞成C语言设计报告中讨论的功能请求!github.com/dotnet/csharplang/issues/188(Github.com/dotnet/csharplang/issues/188)
我认为这对C建筑师来说是个糟糕的判断。局部变量的只读修饰符有助于维护程序的正确性(就像断言一样),并可能帮助编译器优化代码(至少在其他语言的情况下)。事实上,它现在在C中是不允许的,这是另一个论点,即C的一些"特性"仅仅是对其创建者的个人编码风格的强制执行。
- 我同意"把程序员从他自己身上拯救出来"这一部分,但对于帮助编译器优化代码,我持这样的立场,即编译器可以很好地发现一个变量在方法的过程中是否会发生变化,并相应地进行优化。将一个"readonly"标志放在优化器为此目的而识别的任何东西之前并不真正有益,但可能会误导。
- @Cornelius我同意有观点认为,在某些情况下,编译器使用数据流图来计算优化机会,而不考虑任何关键字/修饰符。但是,避免程序员自己编写错误的或者不必要的未经优化的代码可能会为编译器打开优化的机会。
在处理jared的答案时,它可能只是一个编译时特性——编译器会禁止您在初始声明(必须包括赋值)之后写入变量。
我能从中看到价值吗?说实话,这是潜在的,但不是很多。如果您不能很容易地判断是否要在方法的其他地方分配变量,那么您的方法太长了。
对于它的价值而言,Java具有这个特性(使用EDCOX1×0的修改器),除了它必须被用来允许变量被一个匿名的内部类捕获,并且在使用它的地方,它给了我一个混乱的印象,而不是有用的信息。
- 我同意它的有用性。这是我们在讨论中提到的要点之一。如果readonly确实有用,则可能需要重构。
- 在您的方法中,通过sight和编译器查看变量是否被修改是有区别的。我不反对编写一个方法,声明我不修改变量的意图,并让编译器在我不小心这样做的时候通知我(也许一个月后会出现打字错误)!
- 另一方面,在f中,默认情况下,所有变量都是只读的,如果希望能够更改它们,则必须使用"mutable"关键字。因为f是一种.NET语言,所以我想它会进行您描述的编译时检查。
- @A.rex:问题是,让编译器进行这种检查的好处是否值得在阅读代码而实际上不关心代码的情况下进行额外的"侥幸"。
- 考虑下面的定义int arr[] = new int[numItems];,它放在一个循环前面,这个循环将arr传递给一个不熟悉的方法。即使很明显arr总是指向同一个数组,也可能不清楚这是因为不需要将它指向其他地方,还是因为被调用的方法依赖于它始终是同一个实例。如果将来的代码要求代码处理不同大小的数组,而这些数组事先不知道,那么这种区别可能会变得很重要。
- 关于Java中的EDCOX1×11Ω。它必须在创建使用外部作用域(final)变量的本地匿名类时使用…有很好的理由:关闭。
- @克丽斯纳什:是的,我的意思是我很少看到它用在不必要的地方。将编辑以澄清这一点。
- @乔尔穆勒哇,我奇怪地嫉妒现在的F程序员。
- 嗯,我一直使用Java中的"魔法数",它可以适用于方法或类。我只是想在C中做同样的事情,但显然这是不可能的,我不明白为什么不给出这个常见的用例。我真的对不可变的魔法数字感到恶心。是的,我知道我不会修改它们,只是感觉不对。
- @穆罕迪斯:你的意思还不太清楚。你可以在一个方法中使用const,如果这是你想要的…
- @乔斯基特啊,对了,我想的不是真正的神奇数字。如final int OffsetA = this.BaseOffset + 1412。我可以用const来表示1412,是的,但是这样就破坏了为可读性提供单独变量的目的。在这种情况下,我不想要一个const,只是一些只读的东西,即不可变的。实现不符合逻辑,这很令人沮丧。
- @穆罕迪斯:是的,我和你在一起(尽管我不认为你的意思是不可变的)。我不能说这是我特别怀念的事情——如果方法适当地短,那么就不难避免改变所讨论变量的值。我认为有一个建议,有一个等效的var,但一个只读变量…但我不认为发生了。
- -1"我不希望看到一个减少意大利面方法影响的语言特性"这个支持汇编语言编程的论点是荒谬的,并且在某种程度上使读者信服,它具有负面价值。但是,C++中相应的特性是,它应该是默认的。
- @拉拉森德赫。——阿尔夫:我不明白我在哪里提倡"汇编语言编程"。正如我所说,这是有价值的——但我认为现在没有理由包含进来。
- @Jonskeet:由于同样的论点,对可能操作的约束"减少了意大利面条代码的影响",同样适用于静态类型、控制流构造等的使用。正如我所写,这是荒谬的。不是有效的技术参数。
- @拉拉森德。-阿尔夫:我认为"荒谬"太强了,但我同意这并不伟大。将删除它。
- 0删除了向下投票。谢谢。
- fwiw,scala将局部readonly/final值与具有val和var关键字的变量区分开来。在scala代码中,本地vals的使用非常频繁(事实上,它比本地vars更受欢迎)。我猜想EDCOX1 6修改器在Java中不经常使用的主要原因是一个"杂波和B"懒惰。
- 如果局部变量在默认情况下是只读的,并且您必须说"可写"或其他什么东西才能再次写入它们,那么(也许)更好的方法是
- @萨瓦金:是的。有人考虑过使用val,它与var类似,但是只读的。我认为问题是,它与var有点太接近了。
- 对于闭包中不使用的局部变量,readonly不会太重要。另一方面,对于闭包中使用的局部变量,readonly在许多情况下会让编译器生成更高效的代码。目前,当执行进入包含一个闭包的块时,编译器必须为关闭的over变量创建一个新的堆对象,即使从来没有执行过使用闭包的代码。如果变量是只读的,则闭包外部的代码可以使用普通变量;仅当为闭包创建委托时…
- …是否有必要将本地变量复制到新的堆对象中。此外,如果一个方法包含一个使用两个变量的lambda,其中一个变量标识一个小的/便宜的对象,另一个变量标识一个大的/昂贵的对象,而另一个lambda只使用小的/便宜的对象,则使后一个委托保持活动状态的任何代码也将使昂贵的对象保持活动状态。声明任意一个引用readonly将允许编译器中断连接。
- 抱歉,但是"编译器将禁止您在初始声明之后写入变量"远远不够。关于C++的正确性的思考引出了一些复杂的例子:你可以通过引用来传递它吗?方法完全可能需要引用而不是副本,因为它需要查看回调所做的更改。然而,跨越语言边界的参数是可见的,CLR没有语法来标记EDCOX1×18参数只读类似于C++ EDCOX1(19)。您可以使用哪些成员函数?你能调用属性getter吗?
- 如果你把只读设置得很浅,那么最后两个问题很容易(是的),但令人惊讶。而且似乎不允许通过引用传递,就像不允许在initonly字段中那样,尽管这在形式上并不正确。但任何这样的特性都不仅仅涉及到任务。
- 如果我们可以使用来自一元查询理解的let关键字来声明局部变量,我个人会喜欢它。和var一样,只有它是只读的。我不知道这是否会使编译器变得更复杂,或者这可能是一个突破性的变化。
- 至于Java,你很少看到"最终"变量,因为它意味着额外的打字和代码混乱(我也不会使用它)。这将与在C(已经存在于LINQ表达式中)中使用"let"关键字而不是"var"非常不同。
- 实施起来并不难。以下是如何做到这一点的方法:-修改Roslyn编译器,使其在接受"var"的任何地方都接受"let"关键字。下面是一个类似的编译器修改示例(接受不同的字符串起始/终止字符):blogs.msdn.microsoft.com/csharpfaq/2014/04/03/…-编写一个分析器,在修改用"let"关键字声明的变量时给出错误(Google"Roslyn Analyzer")-将两个组件发送给Microsoft;)
- 使用const编写javascript的经验使我在c中寻找它。在编写JavaScript时,我总是缺少C功能。这是我第一次在c_中缺少javascript功能。
C 7设计团队简要讨论了的建议只读局部变量和参数。2015年1月21日设计会议记录:
Parameters and locals can be captured by lambdas and thereby accessed concurrently, but there's no way to protect them from shared-mutual-state issues: they can't be readonly.
In general, most parameters and many locals are never intended to be assigned to after they get their initial value. Allowing readonly on them would express that intent clearly.
One problem is that this feature might be an"attractive nuisance". Whereas the"right thing" to do would nearly always be to make parameters and locals readonly, it would clutter the code significantly to do so.
An idea to partly alleviate this is to allow the combination readonly var on a local variable to be contracted to val or something short like that. More generally we could try to simply think of a shorter keyword than the established readonly to express the readonly-ness.
继续讨论C语言设计报告。投票表示支持。https://github.com/dotnet/csharplang/issues/188
一个原因是没有对只读本地的clr支持。readonly转换为clr/cli initonly操作码。此标志只能应用于字段,对本地字段没有意义。实际上,将它应用到本地可能会产生无法验证的代码。
这并不意味着C不能这样做。但它会给同一语言结构赋予两种不同的含义。局部变量的版本将没有与clr等效的映射。
- 它实际上与对该特性的cli支持无关,因为局部变量决不会向其他程序集公开。cli需要支持字段的readonly关键字,因为它的效果对其他程序集可见。这意味着变量在编译时在方法中只有一个赋值。
- 我认为你刚刚把问题转移到为什么clr不支持这个问题,而不是提供它背后的理性。它允许常量局部变量,因此也可以期望只读局部变量。
- 其中一个例子是在using语句中定义的变量。它们是本地的…和只读(尝试分配它们,C将添加一个错误)。
- 在C++中,1没有EDOCX1×1的机器代码支持(在C++中,它类似于C×EDCOX1,0),而不是像C’EDCOX1(1)那样,尽管它可以同时扮演两个角色。但是C++支持EDCOX1对局部自动变量1。因此,缺少对局部变量c_readonly的clr支持是不相关的。
- 1。这很容易是编译器的特性,就像C++一样。clr支持完全无关。机器装配也不支持它,那又怎样?2。可能会产生无法验证的代码——我不知道怎么做,但也许我弄错了。三。对于同一种语言结构,它会给出两种不同的含义——我怀疑任何人都会把这看作一个问题,因为using和out正是这样做的,世界并没有崩溃。
- 只读变量不是一种甜蜜的矛盾修饰吗?我投票赞成"不变的堆栈元素"。=)
我是那个同事,不友好!(只是开玩笑)
我不会删除这个特性,因为最好写一些简短的方法。这有点像说你不应该使用线程,因为它们很难使用。把刀给我,让我负责不割自己。
就我个人而言,我想要另一个"var"类型的关键字,比如"inv"(入侵)或"rvar",以避免混乱。我最近一直在学习f,发现不变的东西很吸引人。
从来不知道爪哇有这个。
这是对C语言设计师的监督。f有val关键字,它基于clr。没有理由C不能具有相同的语言功能。
我希望本地只读变量的方式与我喜欢本地常量变量的方式相同。但它比其他主题的优先级要低。也许它的优先权是C设计师不这样做的同样原因(还没有!)实现此功能。但在将来的版本中,支持本地只读变量应该很容易(并且向后兼容)。
readonly表示实例变量只能在构造函数中设置。当在本地声明一个变量时,它没有实例(它只是在范围内),并且它不能被构造函数触及。
- 这就是C中"只读"的当前含义,但这不是问题所在。"只读"有一个英语含义,似乎对局部变量有直观的应用程序:您不能写入它(在初始化之后)。这似乎非常像应用于实例变量时的含义,那么为什么(如在证明中,我认为)我们不能将其应用于局部变量呢?
我知道,这并不能回答你的问题。不管怎样,那些阅读这个问题的人可能会欣赏下面的代码。
如果在覆盖一个只应设置一次的局部变量时,您真的关心如何用脚射自己,并且您不想让它成为一个更全局可访问的变量,那么您可以这样做。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class ReadOnly<T>
{
public T Value { get; private set; }
public ReadOnly(T pValue)
{
Value = pValue;
}
public static bool operator ==(ReadOnly<T> pReadOnlyT, T pT)
{
if (object.ReferenceEquals(pReadOnlyT, null))
{
return object.ReferenceEquals(pT, null);
}
return (pReadOnlyT.Value.Equals(pT));
}
public static bool operator !=(ReadOnly<T> pReadOnlyT, T pT)
{
return !(pReadOnlyT == pT);
}
} |
示例用法:
1 2 3 4 5 6 7
| var rInt = new ReadOnly<int>(5);
if (rInt == 5)
{
//Int is 5 indeed
}
var copyValueOfInt = rInt .Value;
//rInt.Value = 6; //Doesn't compile, setter is private |
也许没有rvar rInt = 5那么少的代码,但它可以工作。
- 这没用。变量"var"的这个问题是:var five=5 five=6;断言(five==5)
C已经有了一个只读var,尽管其语法有所不同:
考虑以下几行:
1 2 3
| var mutable = myImmutableCalculationMethod();
readonly var immutable = mutable; // not allowed in C# 8 and prior versions
return immutable; |
比较:
1 2 3
| var mutable = myImmutableCalculationMethod();
string immutable() => mutable; // allowed in C# 7
return immutable(); |
诚然,第一个解决方案可能是减少代码的编写量。但在引用变量时,第二个代码段将使只读显式化。
如果使用C交互式编译器csi,则可以在C中声明只读局部变量:
1 2 3 4 5 6 7 8
| >"C:\Program Files (x86)\MSBuild\14.0\Bin\csi.exe"
Microsoft (R) Visual C# Interactive Compiler version 1.3.1.60616
Copyright (C) Microsoft Corporation. All rights reserved.
Type"#help" for more information.
> readonly var message ="hello";
> message ="goodbye";
(1,1): error CS0191: A readonly field cannot be assigned to (except in a constructor or a variable initializer) |
您还可以使用.csx脚本格式声明只读局部变量。
- 根据错误消息,message在这里不是一个变量,它被编译成一个字段。这不是吹毛求疵,因为交互C中也存在明显的区别:int x; Console.WriteLine(x)是合法的交互C(因为x是一个字段并隐式初始化),但void foo() { int x; Console.WriteLine(x); }不是(因为x是一个变量并在分配之前使用)。另外,Expression> y = x; ((MemberExpression) y.Body).Member.MemberType将揭示x实际上是一个字段,而不是一个局部变量。
使用const关键字生成只读变量。
参考:https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/const
1 2 3 4 5 6 7 8
| public class SealedTest
{
static void Main()
{
const int c = 707;
Console.WriteLine("My local constant = {0}", c);
}
} |
- const和readonly不是一回事。
- 我们感兴趣的是javascript风格的const,其中变量只能在初始化期间分配,而不是csharp风格的const,其中只能使用编译时表达式。例如,您不能执行const object c = new object();,但readonly本地允许您执行此操作。
我认为这是因为有一个只读变量的函数可能永远不会被调用,而且它可能有一些超出范围的内容,您什么时候需要这样做?