为什么C#禁止只读局部变量?

问题描述 投票:102回答:13

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

c# immutability language-design readonly
13个回答
15
投票

一个原因是对于只读本地没有CLR支持。 Readonly被翻译成CLR / CLI initonly操作码。此标志只能应用于字段,对本地没有意义。实际上,将其应用于本地可能会产生无法验证的代码。

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


0
投票

如果您使用的是C#交互式编译器csi,则可以在C#中声明只读局部变量:

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


0
投票

c#已经有一个readonly 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显式。


-3
投票

我认为这是因为一个具有readonly变量的函数可能永远不会被调用,并且可能有一些关于它超出范围的东西,你需要什么时候?


-3
投票

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

55
投票

我认为这对C#架构师来说是一个糟糕的判断。局部变量的readonly修饰符有助于维护程序的正确性(就像断言一样)并且可以帮助编译器优化代码(至少在其他语言的情况下)。现在它在C#中被禁止的事实是另一个论点,即C#的一些“特征”仅仅是对其创作者的个人编码风格的执行。


34
投票

解决Jared的答案,它可能只是一个编译时功能 - 编译器会禁止你在初始声明后写入变量(必须包含一个赋值)。

我能看到这个价值吗?可能 - 但不是很多,说实话。如果您无法轻易判断变量是否将在方法中的其他位置分配,那么您的方法太长。

对于它的价值,Java有这个功能(使用final修饰符),我很少看到它用于除了必须用于允许变量被匿名内部类捕获的情况 - 以及它在哪里使用它,它给我一个杂乱而不是有用信息的印象。


21
投票

C#7设计团队简要讨论过readonly locals and parameters提案。来自C# Design Meeting Notes for Jan 21, 2015

lambdas可以捕获参数和本地,从而同时访问它们,但是没有办法保护它们免受共享 - 共同状态问题的影响:它们不能只读。

通常,大多数参数和许多局部变量在获得初始值后从不打算分配。只读它们就可以清楚地表达这一意图。

一个问题是这个特征可能是“有吸引力的麻烦”。虽然“正确的事情”几乎总是要使参数和本地人只读,但这样做会使代码混乱。

部分缓解这一点的想法是允许将局部变量上的readonly var组合缩小为val或类似的短路。更一般地,我们可以尝试简单地考虑比建立的只读更短的关键字来表达读取性。

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


7
投票

我是那个同事而且不友善! (开玩笑)

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

就个人而言,我想要另一个“var”类型的关键字,如“inv”(invarient)或“rvar”,以避免混乱。我一直在研究F#,发现不可改变的东西很有吸引力。

从来不知道Java有这个。


5
投票

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


5
投票

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


3
投票

Readonly意味着可以在构造函数中设置实例变量的唯一位置。在本地声明变量时,它没有实例(它只在范围内),并且构造函数无法触及它。


1
投票

我知道,这并没有回答你问题的原因。无论如何,那些阅读这个问题的人可能会欣赏下面的代码。

如果你真的担心在重写一个只应该设置一次的局部变量时自己在脚下射击,并且你不想让它成为一个更全局可访问的变量,你可以做这样的事情。

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

© www.soinside.com 2019 - 2024. All rights reserved.