为什么C#不允许只读局部变量?


115

与同事对此进行了友好的辩论。我们对此有一些想法,但是想知道SO人群对此有何看法?


4
@ColonelPanic C和C ++具有const局部变量,您可以使用运行时计算的值对其进行初始化。
Crashworks

1
JavaScript 2015(ES6)具有const类型。例如{const myList = [1,2,3]; }。使用此构造是非常好的编程习惯。更多信息:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…–
andrew.fox

1
对于那些感兴趣的人,有一个针对此功能的UserVoice建议。目前只有87票,因此,如果您想查看本地只读变量,请加油!
伊恩·肯普

1
这不仅仅是语言问题。在C#社区(包括评分最高的C#专家)中,大多数人不关心const正确性及其相关性是一个问题。抵抗是徒劳的。
帕特里克·弗里伯格

1
2017年更新:请对C#语言设计库中正在讨论的功能请求进行投票!github.com/dotnet/csharplang/issues/188
上校恐慌

Answers:


15

原因之一是不支持只读本地CLR。只读转换为CLR / CLI初始化操作码。该标志只能应用于字段,对本地没有意义。实际上,将其应用于本地可能会产生无法验证的代码。

这并不意味着C#无法做到这一点。但这会给同一种语言结构带来两种不同的含义。当地人的版本将没有CLR等效映射。


57
实际上,它与CLI对该功能的支持无关,因为本地变量绝不会暴露给其他程序集。readonlyCLI需要支持字段的关键字,因为其效果对于其他程序集可见的。这只意味着变量在编译时在方法中只有一个分配。
Sam Harwell

16
我认为您已经将问题转移到了为什么CLR不支持这一点,而不是提供其背后的理由。它确实允许使用const locals,因此合理地期望只读的locals也将是合理的。
乍得·舍金斯

9
一个示例是在using语句中定义的变量。它们是本地的...并且是只读的(尝试分配它们,C#将添加错误)。
Softlion

7
-1在C ++中不支持任何机器代码const(在C ++中,这两个角色都可以像C#而readonly不是C#const)。但是C ++支持const局部自动变量。因此,缺少CLR readonly对局部变量C#的支持是无关紧要的。
干杯和健康。-Alf

5
1.这很容易成为编译器功能,就像在C ++中一样。CLR支持完全不相关。机器组装也不支持它,那又如何呢?2. (可能)会产生无法验证的代码 -我不知道如何,但也许我弄错了。3. 这将给同一种语言结构带来两种不同的含义 -我怀疑有人会将此视为一个问题,因为using并且out正是如此,世界并没有崩溃。

66

我认为对于C#架构师来说这是一个错误的判断。局部变量上的readonly修饰符有助于维护程序的正确性(就像断言一样),并且有可能帮助编译器优化代码(至少在其他语言中)。目前在C#中不允许使用该事实的另一个说法是,C#的某些“功能”仅仅是其创建者的个人编码风格的一种实现。


11
我同意“将程序员从他自己那里救出来”这一部分,但是对于帮助编译器优化代码,我持有一种立场,即编译器可以很好地找出变量是否在方法过程中发生变化并相应地进行优化。无论哪种方式。在优化器为此目的仍能识别的内容之前放置“只读”标志并没有真正的好处,但有可能导致误导。
Cornelius

1
@Cornelius我同意有观点认为,在某些情况下,编译器使用数据流图来找出优化机会,而与任何关键字/修饰符无关。但是,使程序员免于编写错误的代码以及避免不必要地进行未优化的代码可能会给编译器带来这种优化机会。
舒瓦

现代编译器是否仍不执行静态单一分配?在这种情况下,就优化而言,这是没有意义的(但是,如果编译器支持SSA,则意味着一次分配一次局部变量也很简单)。

33

解决Jared的答案时,它可能只需要具有编译时功能-编译器将禁止您在初始声明(必须包含赋值)之后写入变量。

我可以在其中看到价值吗?坦率地说,可能-但不是很多。如果您不容易确定是否要在方法中的其他位置分配变量,则您的方法太长了。

对于它的价值,Java有此功能(使用final修改器),我已经非常难得一见它使用比在其情况下,其他被用来使变量通过一个匿名内部类被捕获-并且它使用它给我的印象是混乱而不是有用的信息。


75
通过视觉编译器查看变量是否在您的方法中被修改之间是有区别。我不反对编写方法,声明不修改变量的意图以及在我不小心做时让编译器通知我(可能在一个月后输入错字)!
A. Rex

50
另一方面,在F#中,所有变量默认情况下都是只读的,如果要更改它们,必须使用'mutable'关键字。由于F#是.NET语言,因此我想它会执行您描述的编译时检查。
乔尔·穆勒

2
@ A.Rex:问题实际上是让编译器执行该检查的好处是否值得在阅读代码而不实际关心它时获得额外的“绒毛”。
乔恩·斯基特

3
FWIW,Scala 通过其和关键字将局部readonly/ final值与变量区分开。在Scala代码中,local 的使用非常频繁(实际上,它们比local 的首选)。我怀疑Java中不经常使用修饰符的主要原因是a)混乱和b)懒惰。valvarvalvarfinal
Aaron Novstrup 2014年

4
对于没有在闭包中使用的局部变量,readonly不会太重要。另一方面,对于在闭包中使用的局部变量,readonly在许多情况下会让编译器生成更有效的代码。当前,当执行进入包含闭包的块时,即使从未执行过任何使用闭包的代码,编译器也必须为封闭变量创建一个新的堆对象。如果变量是只读的,则闭包外部的代码可以使用普通变量;仅当为关闭创建了代表...
超级猫

30

C#7设计团队简要讨论了一个建议的只读局部变量和参数。摘自2015年1月21日的C#设计会议笔记

Lambda可以捕获参数和局部变量,从而可以同时访问这些参数和局部变量,但是无法保护它们免受共享互状态问题的影响:它们不能是只读的。

通常,绝大部分参数和许多局部变量在获得初始值后都不会分配给它们。允许对它们进行只读操作将清楚地表达该意图。

一个问题是该功能可能是“有吸引力的麻烦”。要做的“正确的事”几乎总是使参数和局部变量成为只读的,而这样做会使代码混乱。

部分缓解此问题的想法是允许将局部变量上的只读只读组合var压缩为val或类似的缩写。通常,我们可以尝试简单地考虑比已建立的readonly短的关键字来表达readonly-ness。

C#语言设计库中的讨论仍在继续。投票表示支持。https://github.com/dotnet/csharplang/issues/188


也可以将readread设为默认值(通过某些选项或关键字或其他方式)。大多数变量和参数应该是只读的,只有少数可写。尽量减少可写的数量通常是一件好事。
Dave Cousineau

12

它是C#语言设计师的疏忽。F#具有val关键字,它基于CLR。没有理由C#不能具有相同的语言功能。


7

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

我不会消除该功能,因为编写简短的方法更好。这有点像说您不应该使用线程,因为它们很难。给我刀,让我负责不割伤自己。

我个人希望使用另一个“ var”类型的关键字,例如“ inv”(不变)或“ rvar”,以避免混乱。我最近一直在研究F#,发现不变的东西很吸引人。

从来不知道Java有这个。


5

我想要本地只读变量的方式与我喜欢本地const变量的方式相同。但是它没有其他主题优先。
也许它的优先级与C#设计人员尚未实现此功能的原因相同。但是在将来的版本中支持本地只读变量应该很容易(并且向后兼容)。


2

只读表示只能在构造函数中设置实例变量。在本地声明变量时,它没有实例(仅在作用域内),并且构造函数无法触及它。


6
这就是C#中“只读”的当前含义,但这不是问题。“只读”具有英文含义,似乎可以直观地应用到局部变量:您无法对其进行写入(在初始化之后)。这似乎很像应用于实例变量时的含义,那么为什么我们不能将其应用于局部变量呢?
Spike0xff

0

我知道,这不能回答您问题的原因。无论如何,那些阅读此问题的人可能还是喜欢下面的代码。

如果您真的想在覆盖仅应设置一次的局部变量时用脚踩自己的脚,并且不想使其成为更易于全局访问的变量,则可以执行以下操作。

    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但它可以工作。


这对这里没有帮助。变量'var'的问题是:{var五= 5五= 6;Assert.That(five == 5)}
Murray

0

如果使用的是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脚本格式声明只读局部变量。


5
根据错误消息,message这里不是变量,而是编译为字段。这不是挑剔的,因为这种区别在交互式C#中也明确存在:int x; Console.WriteLine(x)是合法的交互式C#(因为x是一个字段并隐式初始化),但void foo() { int x; Console.WriteLine(x); }不是(因为x是一个变量,并且在赋值之前就使用过)。同样,Expression<Func<int>> y = x; ((MemberExpression) y.Body).Member.MemberType将揭示这x实际上是一个字段,而不是局部变量。
Jeroen Mostert

0

c#已经有一个只读的var,尽管语法有所不同:

考虑以下几行:

var mutable = myImmutableCalculationMethod();
readonly var immutable = mutable; // not allowed in C# 8 and prior versions
return immutable;

与之比较:

var mutable = myImmutableCalculationMethod();
string immutable() => mutable; // allowed in C# 7
return immutable();

诚然,第一个解决方案可能是编写更少的代码。但是,第二个代码段在引用变量时将使只读显式。


那不是“只读”的。从代码访问的角度来看,这是一个返回捕获变量的局部函数..因此,大量不必要的开销和丑陋的语法甚至无法使基础变量变为只读。另外,“可变”通常适用于对象,而不适用于局部变量readonly var im = new List<string>(); im.Add("read-only variable, mutable object!");
user2864740

-2

使用const关键字使只读变量。

参考:https : //docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/const

public class SealedTest
{
    static void Main()
    {
        const int c = 707;
        Console.WriteLine("My local constant = {0}", c);
    }
}

1
我们对JavaScript样式感兴趣,在JavaScript样式const中,只能在变量的初始化期间分配变量,而对csharp样式不感兴趣,在csharp样式中const,只能使用编译时表达式。例如,您不能做,const object c = new object();但是readonly当地人会允许您这样做。
binki

-5

我认为这是因为可能永远不会调用具有只读变量的函数,并且该函数可能超出范围,什么时候需要?

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.