如何在C#中声明不可变变量(值)?

时间:2017-03-21 00:14:36

标签: c# .net syntax immutability

在Scala中,我可以写(这意味着它在C#中意味着完全相同)

var v = 1;
v = 2;

但是不能写(好吧,当然我可以编写,但实际上虽然语法正确但无法编译)

val v = 1;
v = 2;

分号不是必需的,但可以在Scala中自愿使用,所以我决定将它们包含在内,让代码更紧密地与C#相对应。 val表示一个不可变值,一种只能分配一次的变量,但不太可能c#const s可以用运行时表达式的结果初始化,不太可能是C#{{ 1}}字段可以在代码中的任何位置引入,其中readonly变量可以并且不太可能使C#不可变类型对引用替换不敏感,而不仅仅是对引用对象的修改。

我喜欢C#在每种新版语言中引入越来越多功能性编码糖果的方式,但却错过了这一点(可以说是最简单也是最重要的)。在大多数情况下,我只将值赋给我的变量一次,因此重新赋值通常是不会发生的事情,这意味着一个不期望的事情并且可能以这种方式导致错误。也许,我可能只是不知道这样的功能?我不介意这样的声明看起来有点笨拙(可能是一些F#导入,无论它们在C#代码中看起来像什么)。

更新:由于现在看来确实没有这样的功能(2017年3月,C#语言版本7.0),并且正如其他人所建议的那样,我提交了issue at the C# language design GitHub repository。< / p>

1 个答案:

答案 0 :(得分:5)

基本上,你不能 - 至少在C#6时 - 有一个值得注意的例外,如下所述。也许在未来的C#版本中会有一些变化。 C#7有“记录类型”的计划,它可以在与匿名内联对象配对时打开一些方式。但是,我实际上不知道在C#7中确切添加了什么。

对此类事物的唯一正常支持是在类成员范围:

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 - 好吧,应该或多或少显而易见。

在普通代码的范围内,您创建的任何“变量”(与上面的“成员”或“您提到的”常量“相对)(几乎)总是 writable,赋值语义总是根据变量类型的种类(结构/类)而不同。

编辑:我找到了一个!你关于笨拙语法的注释让我有了一个想法。实际上, 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);
}

元组的字段/属性是不可写的,因此这样的元组文字将非常方便,除了要写的'pair2'额外标识符(以及创建和处理元组对象的一些成本)

但是,我实际上不知道是否可变。它们被称为“元组”,因此我立即想到“元组&lt;&gt;”其属性是只读的,但是in this old article

  

元组是值类型,它们的元素只是公共的可变字段。

现在,我还没有安装VS2017 ..这需要一些时间,也许别人可以比我早点检查。