关于immutability:为什么C#不允许只读局部变量?

Why does C# disallow readonly local variables?

与同事就此事进行友好的辩论。我们对此有一些想法,但想知道这么多人是怎么想的?


我认为这对C建筑师来说是个糟糕的判断。局部变量的只读修饰符有助于维护程序的正确性(就像断言一样),并可能帮助编译器优化代码(至少在其他语言的情况下)。事实上,它现在在C中是不允许的,这是另一个论点,即C的一些"特性"仅仅是对其创建者的个人编码风格的强制执行。


在处理jared的答案时,它可能只是一个编译时特性——编译器会禁止您在初始声明(必须包括赋值)之后写入变量。

我能从中看到价值吗?说实话,这是潜在的,但不是很多。如果您不能很容易地判断是否要在方法的其他地方分配变量,那么您的方法太长了。

对于它的价值而言,Java具有这个特性(使用EDCOX1×0的修改器),除了它必须被用来允许变量被一个匿名的内部类捕获,并且在使用它的地方,它给了我一个混乱的印象,而不是有用的信息。


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等效的映射。


我是那个同事,不友好!(只是开玩笑)

我不会删除这个特性,因为最好写一些简短的方法。这有点像说你不应该使用线程,因为它们很难使用。把刀给我,让我负责不割自己。

就我个人而言,我想要另一个"var"类型的关键字,比如"inv"(入侵)或"rvar",以避免混乱。我最近一直在学习f,发现不变的东西很吸引人。

从来不知道爪哇有这个。


这是对C语言设计师的监督。f有val关键字,它基于clr。没有理由C不能具有相同的语言功能。


我希望本地只读变量的方式与我喜欢本地常量变量的方式相同。但它比其他主题的优先级要低。也许它的优先权是C设计师不这样做的同样原因(还没有!)实现此功能。但在将来的版本中,支持本地只读变量应该很容易(并且向后兼容)。


readonly表示实例变量只能在构造函数中设置。当在本地声明一个变量时,它没有实例(它只是在范围内),并且它不能被构造函数触及。


我知道,这并不能回答你的问题。不管怎样,那些阅读这个问题的人可能会欣赏下面的代码。

如果在覆盖一个只应设置一次的局部变量时,您真的关心如何用脚射自己,并且您不想让它成为一个更全局可访问的变量,那么您可以这样做。

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那么少的代码,但它可以工作。


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脚本格式声明只读局部变量。


使用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);
    }
}


我认为这是因为有一个只读变量的函数可能永远不会被调用,而且它可能有一些超出范围的内容,您什么时候需要这样做?