与同事就这件事进行了友好的辩论。我们对此有一些想法,但想知道 SO 人群对此有何看法?
13 回答
我认为这是对部分 C# 架构师的错误判断。局部变量上的 readonly 修饰符有助于保持程序的正确性(就像断言一样),并且可以潜在地帮助编译器优化代码(至少在其他语言的情况下)。它现在在 C# 中被禁止的事实是另一个论点,即 C# 的某些“功能”仅仅是对其创建者的个人编码风格的强制执行。
C# 7 设计团队简要讨论了只读局部变量和参数的提案。来自2015 年 1 月 21 日的 C# 设计会议笔记:
参数和局部变量可以被 lambdas 捕获,从而并发访问,但是没有办法保护它们免受共享互状态问题的影响:它们不能是只读的。
通常,大多数参数和许多局部变量在获得初始值后永远不会被分配。允许只读它们会清楚地表达这种意图。
一个问题是,此功能可能是“有吸引力的麻烦”。尽管“正确的做法”几乎总是将参数和局部变量设为只读,但这样做会使代码变得非常混乱。
部分缓解这种情况的一个想法是允许将局部变量上的组合 readonly var 收缩为 val 或类似的东西。更一般地,我们可以尝试简单地考虑一个比已建立的 readonly 更短的关键字来表达 readonly-ness。
在 C# 语言设计存储库中继续讨论。投票表示您的支持。https://github.com/dotnet/csharplang/issues/188
针对 Jared 的回答,它可能只是一个编译时特性——编译器会禁止您在初始声明后写入变量(这必须包括一个赋值)。
我能看到其中的价值吗?老实说,有可能——但不是很多。如果你不能轻易判断一个变量是否要在方法的其他地方分配,那么你的方法太长了。
对于它的价值,Java 有这个特性(使用final
修饰符),我很少看到它被使用,除非它必须用于允许变量被匿名内部类捕获 - 以及它在哪里使用时,它给我的印象是混乱而不是有用的信息。
这是对 c# 语言设计器的疏忽。F# 有 val 关键字,它基于 CLR。没有理由 C# 不能具有相同的语言功能。
一个原因是只读本地没有 CLR 支持。Readonly 被翻译成 CLR/CLI initonly 操作码。此标志只能应用于字段,对本地没有意义。事实上,将其应用于本地可能会产生无法验证的代码。
这并不意味着 C# 不能做到这一点。但它会给同一个语言结构赋予两种不同的含义。本地版本将没有 CLR 等效映射。
我是那个同事,不友好!(只是在开玩笑)
我不会消除该功能,因为最好编写简短的方法。这有点像说你不应该使用线程,因为它们很难。把刀给我,让我负责不割伤自己。
就个人而言,我想要另一个“var”类型的关键字,如“inv”(不变)或“rvar”,以避免混乱。我最近一直在研究 F#,发现不可变的东西很有吸引力。
从来不知道Java有这个。
我想要本地只读变量,就像我喜欢本地const变量一样。但它的优先级低于其他主题。
也许它的优先级与 C# 设计者不(还!)实现此功能的原因相同。但是在未来的版本中支持本地只读变量应该很容易(并且向后兼容)。
只读意味着可以设置实例变量的唯一位置是在构造函数中。在本地声明变量时,它没有实例(它只是在范围内),并且构造函数无法触及它。
我知道,这并不能回答您的问题的原因。无论如何,那些阅读这个问题的人可能会欣赏下面的代码。
如果你真的担心在覆盖一个只应该设置一次的局部变量时,你会自责,并且你不想让它成为一个更全局可访问的变量,你可以做这样的事情。
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);
}
}
示例用法:
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
但它可以工作。
如果您使用 C# 交互式编译器,则可以在 C# 中声明只读局部变量csi
:
>"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
脚本格式中声明只读局部变量。
能够将局部变量设为只读可以更容易理解复杂的算法,因为它减少了移动部件的数量。
由于 C# 不为本机提供非编译时常量,因此我将其与隐式强制转换一起使用:
public readonly struct ReadonlyVar<T>
{
private readonly T value;
internal ReadonlyVar(T _value) => value = _value;
public static implicit operator T(ReadonlyVar<T> _readonly) => _readonly.value;
public override string ToString() => "" + value;
}
public static class ReadonlyExt
{
public static ReadonlyVar<T> Readonly<T>(this T _value) => new ReadonlyVar<T>(_value);
}
用法:
int y = 234;
var x = ( 9000 + y ).Readonly();
y = x;
它并不完美,因为可以为它分配另一个 ReadonlyVar,但这从来没有无意中发生在我身上。
使用const
关键字使只读变量。
参考:https ://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/const
public class SealedTest
{
static void Main()
{
const int c = 707;
Console.WriteLine("My local constant = {0}", c);
}
}
我认为这是因为一个具有只读变量的函数可能永远不会被调用,而且它可能会超出范围,你什么时候需要?