【问题标题】:C# readonly vs Java finalC# readonly vs Java final
【发布时间】:2013-02-08 05:35:20
【问题描述】:

在 Java 中,final 表示一个变量只能分配一次,但该分配可以在程序中的任何位置进行。在 C# 中,readonly 意味着一个字段只能在 constructor 中分配,这在 IMO 中是非常有用的。

众所周知,C# 深受 Java 设计的影响,但这种差异一直让我感到非常奇怪。有谁知道 CLR 中是否有技术原因导致 C# 的 readonly 与 Java 的 final 的行为不太有用?

编辑:

响应 cmets;我想指出,我很清楚不变性的好处,而且我到处都在使用它。我相信readonly 没有Java那么有用,因为:

public class Foo 
{
    private readonly int _bar;

    Foo()
    {
        _bar = 5;
    }
}

糟糕,我实际上需要在辅助方法中初始化该值!

public class Foo 
{
    private readonly int _bar;

    Foo()
    {
        initialize()
    }

    private void initialize()
    {
        _bar = 5; //Can't compile because of semantics of readonly
    }     
}

【问题讨论】:

  • 你的“不那么有用”是我的“更安全”。
  • 不变性有什么好处?我想你会发现readonly 对编译器非常有用。你的问题本质上是主观的。
  • 您也不能在 java 程序中的任何地方为最终变量赋值。如果您正在制作一个静态最终常量,那么您需要在现场给出值,否则如果您将变量作为最终值,那么您可以在构造函数中启动它。
  • @JohnSaunders 这不是重复的。他不是在问Java 在 C# 中的 final 的等价物。他表明他已经知道这一点。我认为您没有费心阅读这个问题,因为它完全不同。
  • 这个问题基于一个错误的前提,您可以在类中的任何位置初始化最终的Java 字段。你不能做这个!第二个示例也不会在 Java 中编译(使用 final)。

标签: c# .net clr language-design readonly


【解决方案1】:

这是一个老问题,但我认为值得注意的是,这在 Java 15 中工作:

public class Main
{
   final int testInt;

   public Main()
   {
      init();
   }

   private void init() 
   {
      testInt = 3;
   }
}

java: 变量 testInt 未在默认构造函数中初始化

所以看起来 C# readonly 和 Java final 的行为方式相同。

【讨论】:

    【解决方案2】:

    readonly 的行为有一个技术原因:在创建的程序集的元数据中,该字段被标记为 initonly 属性,这将确保该字段不会在构造函数之外被修改。 1 但是,虽然无法验证,但通过获取只读字段的地址,仍然可以更改其值。可验证的 IL 和 C# 不允许您这样做。

    在编译时不可能对所有方法强制执行此操作,因为编译器必须分析方法可能调用的所有可能顺序。在运行时,如果它必须检查每个字段写入是否之前已写入,则可能会对 CLR 造成负担并对性能产生负面影响。相反,C# 和 CLR 不允许在任何地方为字段赋值,除非在构造函数的仔细分析范围内。

    在我看来,这并没有降低 readonly 关键字的价值。我将它用于仅由构造函数提供值的字段(例如,创建列表或存储构造函数参数)。 C# 将确保我不会再更改该字段,确保我不会意外地将其设置为 null 或任何东西。

    1) 感谢 Eric Lippert 指出这一点。

    【讨论】:

    • @Virtlink 谢谢你的回答。很高兴不是每个人都只是寻找借口来结束事情。
    • 第一段错误。您可以通过反射修改只读字段,仅使用安全的 C# 和可验证的 IL。您甚至可以通过这种方式更改string.Empty 的值。也许您的意思是可信代码,它与verifiable IL 是完全不同的概念。不受信任的代码不能使用(大部分)反射,但可验证的 IL 可以。
    • @Timwi 你是对的,但是当你把反思带入画面时,所有的赌注都没有了。我没有提到它,因为它与问题无关。这让我的第一段没有,最多不完整
    • 奇怪的是 Java final 和 C# 只读,为了这个问题,它们的工作方式完全相同:你不能在类中的任何地方为它们赋值,只能在构造函数中或它的声明
    • @Virtlink 你用的是什么java编译器?查看问题的第二个在 Java 中不起作用的示例:ideone.com/iq1EbV
    猜你喜欢
    • 2012-04-27
    • 2011-08-16
    • 1970-01-01
    • 2020-09-24
    • 2012-01-07
    • 2013-09-02
    • 1970-01-01
    • 2012-10-01
    • 1970-01-01
    相关资源
    最近更新 更多