如何在 C# 中声明不可变变量(值)?
How do I declare an immutable variable (value) in C#?
我可以在 Scala 中编写(它的含义与在 C# 中的含义完全相同)
var v = 1;
v = 2;
但是不会写(好吧,我当然会写,但是虽然语法是正确的,但实际上无法编译)
val v = 1;
v = 2;
分号不是必需的,但可以在 Scala 中自愿使用,因此我决定将它们包括在内,让代码更紧密地对应 C#。 val
表示不可变值,一种只能赋值一次的变量,但不太可能c# const
s可以用运行-time表达式的结果初始化,不太可能到 C# readonly
字段可以在代码中的任何地方引入,其中 var
变量可以并且不太可能 C# 不可变类型不受引用替换的影响,而不仅仅是引用对象的修改。
我喜欢 C# 在该语言的每个新版本中引入越来越多的函数式编码糖果的方式,但非常想念这个(可以说是最简单和最重要的)一个。在大多数情况下,我只为我的变量赋值一次,因此重新赋值通常是不应该发生的事情,这意味着这是意料之外的事情,可能会以这种方式导致错误。也许我只是不知道这样的功能?我不介意这样的声明看起来有点笨拙(可能有一些 F# 导入,无论它们在 C# 代码中看起来像什么)。
更新: 看来现在确实没有这样的功能(2017 年 3 月,C# 语言版本 7.0),正如其他人所建议的,我已经提交了 issue at the C# language design GitHub repository.
基本上,您不能 - 至少在 C#6 的时候 - 除了下面提到的一个值得注意的例外。也许在未来的 C# 版本中会有所改变。有针对 C# 7 的 "record types" 计划,它可以在与匿名内联对象配对时打开一些方式。但是,我实际上并不知道在 C#7 中到底添加了什么。
对类似内容的唯一正常支持是在 class 成员范围内:
class Foo
{
public readonly int shoeSize; // readonly field
public int ShoeSize { get { .. } } // readonly property
public int ToeSize { get; } = 5; // readonly property with initializer
// ..etc
}
只读字段只能在对象成员初始化期间或在构造函数中设置,而 getter - 好吧,应该或多或少是显而易见的。
在正常代码的范围内,您创建的任何“变量”(与上面的 'member' 或您提到的 'constants' 相反)是 (几乎)总是可写,赋值语义将总是根据变量类型的种类(struct/class)而不同。
编辑:我找到了!你关于笨拙语法的注释让我有了一个主意。实际上,foreach 迭代器变量 会防止编译器进行赋值,因此您可以将它与 Enumerable.Repeat 一起使用以快速打开一个只迭代一次的 foreach 作用域..
static void Main()
{
foreach(int x in Enumerable.Repeat(5/*value for X*/, 1/*single run*/))
{
x=4; // <- compile time error!
Console.WriteLine(x);
}
}
EDIT2:另一种选择,更好,tuple literal that is said to be added in C#7
public static void Main()
{
var pair1 = (42, "hello");
System.Console.Write(Method(pair1).message);
var pair2 = (code: 43, message: "world");
System.Console.Write(pair2.message);
}
元组的 fields/properties 是不可写的,因此这样的元组文字将非常方便,除了要写的 'pair2' 额外标识符(以及 .. 创建和处理元组的一些成本元组对象)
但是,我实际上不知道它们是否可变。它们被称为"tuples",所以我立即想到"Tuple<>",其属性是只读的,但是,in this old article
Tuples are value types, and their elements are simply public, mutable fields.
现在,我(还)没有安装 VS2017。这需要一些时间,也许其他人能够比我更快地检查。
我可以在 Scala 中编写(它的含义与在 C# 中的含义完全相同)
var v = 1;
v = 2;
但是不会写(好吧,我当然会写,但是虽然语法是正确的,但实际上无法编译)
val v = 1;
v = 2;
分号不是必需的,但可以在 Scala 中自愿使用,因此我决定将它们包括在内,让代码更紧密地对应 C#。 val
表示不可变值,一种只能赋值一次的变量,但不太可能c# const
s可以用运行-time表达式的结果初始化,不太可能到 C# readonly
字段可以在代码中的任何地方引入,其中 var
变量可以并且不太可能 C# 不可变类型不受引用替换的影响,而不仅仅是引用对象的修改。
我喜欢 C# 在该语言的每个新版本中引入越来越多的函数式编码糖果的方式,但非常想念这个(可以说是最简单和最重要的)一个。在大多数情况下,我只为我的变量赋值一次,因此重新赋值通常是不应该发生的事情,这意味着这是意料之外的事情,可能会以这种方式导致错误。也许我只是不知道这样的功能?我不介意这样的声明看起来有点笨拙(可能有一些 F# 导入,无论它们在 C# 代码中看起来像什么)。
更新: 看来现在确实没有这样的功能(2017 年 3 月,C# 语言版本 7.0),正如其他人所建议的,我已经提交了 issue at the C# language design GitHub repository.
基本上,您不能 - 至少在 C#6 的时候 - 除了下面提到的一个值得注意的例外。也许在未来的 C# 版本中会有所改变。有针对 C# 7 的 "record types" 计划,它可以在与匿名内联对象配对时打开一些方式。但是,我实际上并不知道在 C#7 中到底添加了什么。
对类似内容的唯一正常支持是在 class 成员范围内:
class Foo
{
public readonly int shoeSize; // readonly field
public int ShoeSize { get { .. } } // readonly property
public int ToeSize { get; } = 5; // readonly property with initializer
// ..etc
}
只读字段只能在对象成员初始化期间或在构造函数中设置,而 getter - 好吧,应该或多或少是显而易见的。
在正常代码的范围内,您创建的任何“变量”(与上面的 'member' 或您提到的 'constants' 相反)是 (几乎)总是可写,赋值语义将总是根据变量类型的种类(struct/class)而不同。
编辑:我找到了!你关于笨拙语法的注释让我有了一个主意。实际上,foreach 迭代器变量 会防止编译器进行赋值,因此您可以将它与 Enumerable.Repeat 一起使用以快速打开一个只迭代一次的 foreach 作用域..
static void Main()
{
foreach(int x in Enumerable.Repeat(5/*value for X*/, 1/*single run*/))
{
x=4; // <- compile time error!
Console.WriteLine(x);
}
}
EDIT2:另一种选择,更好,tuple literal that is said to be added in C#7
public static void Main()
{
var pair1 = (42, "hello");
System.Console.Write(Method(pair1).message);
var pair2 = (code: 43, message: "world");
System.Console.Write(pair2.message);
}
元组的 fields/properties 是不可写的,因此这样的元组文字将非常方便,除了要写的 'pair2' 额外标识符(以及 .. 创建和处理元组的一些成本元组对象)
但是,我实际上不知道它们是否可变。它们被称为"tuples",所以我立即想到"Tuple<>",其属性是只读的,但是,in this old article
Tuples are value types, and their elements are simply public, mutable fields.
现在,我(还)没有安装 VS2017。这需要一些时间,也许其他人能够比我更快地检查。