const与readonly关键字

先一针见血指出你的误区

csharp 复制代码
public static partial class NtsHelper
{
    private static readonly GeometryFactory _factory = new GeometryFactory();
    private const double Tolerance = 1e-6;

    public static double GetTolerance()
    {
        return Tolerance;
    }
}

这里 _factoryTolerance 根本就不是「非静态成员」!它们两个全部都是静态成员。


逐条拆解每个字段

1. private static readonly GeometryFactory _factory

  • 修饰符:static
    👉 静态字段 ,属于类 NtsHelper,不属于任何实例对象。
    readonly 只是代表:只能在构造阶段赋值一次,之后不可修改readonly ≠ 实例成员!

很多人踩坑:以为 readonly = 实例变量。

  • static readonly → 静态只读(全局唯一)
  • readonly(不带static)→ 实例只读(每个new出来的对象一份副本)

2. private const double Tolerance = 1e-6;

const 常量天生就是隐式静态的

哪怕你不写 static,编译器也会把它当成静态。

常量永远属于类,不属于对象实例。

所以下面静态方法 GetTolerance() 直接 return Tolerance,完全合法。

3. GetTolerance()

标记了 static,静态方法。

静态方法只能直接访问本类的静态成员,你这里刚好符合规则。


对比两张对照表,彻底分清

✅ 你代码里(全部静态)

csharp 复制代码
//静态只读字段:属于类
private static readonly GeometryFactory _factory;
//常量,天生隐式静态
private const double Tolerance = 1e-6;
//静态方法
public static double GetTolerance(){}

❌真正的非静态(实例)成员,长这样(没有static)

csharp 复制代码
public partial class NtsHelper
{
    //实例字段:必须 new NtsHelper() 创建对象之后才有
    private readonly GeometryFactory _factory;
    private double _myValue;
    
    //实例方法
    public double GetValue()
    {
        return _myValue;
    }
}

⚠️ 注意!static class 静态类,禁止出现任何不带static的实例成员。如果你在静态类写:

csharp 复制代码
public static partial class NtsHelper
{
    private readonly GeometryFactory _factory; //编译报错!!
}

编译器直接红色报错,静态类不能包含实例字段。


回到你项目设计的优点(管网NTS工具类)

csharp 复制代码
private static readonly GeometryFactory _factory = new GeometryFactory();

_factory 全局只实例化一次 ,程序启动就创建好。

所有地方调用NTS几何计算复用同一个GeometryFactory对象,不用每次计算都new一个工厂,节省内存、性能更好。


快速记忆口诀

  1. static readonly:静态只读,一份全局副本。
  2. readonly:实例只读,每个对象一份。
  3. const:天生隐式静态,不需要写static。
  4. 静态类里面所有成员必须带static,编译器强制要求

核心结论先给你,把3条功能责任分清

全局唯一:static 的功劳

只能构造阶段赋值一次 + 运行后不能修改:readonly 的功劳

readonly 确实是「只读」,但不是编译期常量 ,它是「运行时只读锁」。

下面拆开逐个讲清楚。

一、readonly到底是什么?能干2件事

功能1:赋值时机受限

字段只能在两个地方赋值

  1. 声明的时候直接初始化:public readonly double a = 10;
  2. 类的构造函数里面(实例构造函数 / 静态构造函数)

构造执行完毕之后,代码任何地方再也不能给这个字段赋值、修改它

功能2:运行期间禁止改写

构造跑完,你写 xxx = 5; 修改readonly字段,编译器直接报错。

⚠️ 非常重要的误区:

readonly 本身完全不控制"全局唯一"

如果你只写 readonly、不加 static,每 new 一个对象,就会生成一份全新副本!

实例‑readonly(不带static)例子

csharp 复制代码
class Demo
{
    //实例只读字段:每new一个对象,就有一份独立副本
    public readonly double Value;

    public Demo(double v)
    {
        Value = v; //✅构造函数内允许赋值
    }
}

var d1 = new Demo(10);
var d2 = new Demo(20); 
//d1.Value 和 d2.Value 是两份完全独立的数据!

这里只有readonly没有static,不是全局唯一!

二、static 干的是什么?

static 的职责只有一件事:这个字段属于【类本身】,而不属于new出来的对象实例

  • 整个程序内永远只有1份副本,全局唯一
  • static 完全不管你能不能修改!
csharp 复制代码
public static int Count = 0;
Count = 100; //✅随便改,static不锁读写

static只管「几份副本」,根本没有只读、不可修改的能力

三、static + readonly 组合(就是你NtsHelper里面的写法)

csharp 复制代码
private static readonly GeometryFactory _factory = new GeometryFactory();

两个关键字分工清晰,各司其职:

  1. static全局唯一,全程序只有这一个_factory对象,不需要new实例就能访问
  2. readonly赋值锁定,只能初始化那一次,后面代码禁止修改 _factory
特性 由哪个关键字提供
全局唯一、一份副本 static
仅构造阶段可赋值一次 readonly
运行全程不能修改 readonly

分开对比:

  • static:管「有几份」
  • readonly:管「能不能改、什么时候赋值」

四、再把 const 拉进来对比(你代码里还有 const double Tolerance

const = 编译期常量,天生隐式static

csharp 复制代码
private const double Tolerance = 1e-6;
  1. 编译的时候值就被钉死,不能在构造函数赋值;
  2. 天生全局唯一,不能加readonly。

一张终极对比总表

写法 副本数量(全局唯一?) 什么时候能赋值 运行时能否修改
int x; 实例:每个new对象一份 任何地方 可读写
readonly int x; 实例:每个new对象一份 声明处 / 实例构造函数 ❌不可修改
static int x; 全局唯一1份 任何地方 ✅随便读写
static readonly int x; 全局唯一1份 声明处 / 静态构造函数 ❌不可修改
const int x = 1; 全局唯一1份 只能声明时赋值 ❌编译期固定,不可修改

五、结合你NTS管网代码解释

csharp 复制代码
private static readonly GeometryFactory _factory = new GeometryFactory();
  1. static:GeometryFactory 工厂对象全局就创建1个,所有管线几何运算共用它;
  2. readonly:写完之后代码不能再把 _factory = null 或者 _factory = new GeometryFactory(),防止运行时被意外覆盖,引发难以排查的NTS崩溃。

高频踩坑点

  1. ❌误区:readonly = 全局唯一

错!不带static的readonly,每个实例一份。

  1. ❌误区:static = 不能修改

错!static变量默认完全可读写。

  1. ❌误区:static readonlyconst 完全等价

不等价:static readonly 可以在静态构造函数里面动态赋值,const只能写死字面量。

相关推荐
leonkay1 小时前
C# 锁机制——【2】深入原理
开发语言·后端·spring·c#·个人开发
geovindu6 小时前
CSharp: Wordcloud
开发语言·后端·c#·.net·.netcore·词云
geovindu7 小时前
CSharp: Command Pattern
开发语言·后端·c#·.net·.netcore·命令模式·行为模式
格林威10 小时前
C# 相机图像阴影校正:使用OpenCvSharp实现工业相机阴影平场校正功能
人工智能·数码相机·opencv·计算机视觉·c#·机器视觉·工业相机
俊昭喜喜里10 小时前
c#中的构造函数
开发语言·数据库·c#
hahaha601610 小时前
HLS高层次综合设计--axi之burst纠偏
开发语言·算法·fpga开发·c#
点心的游戏开发世界19 小时前
Unity C# 脚本学习笔记:命名空间与 using
学习·unity·c#
淡海水21 小时前
07-04-并发-ConcurrentBag-T-工作窃取WorkStealing算法
开发语言·算法·c#·bag·concurrent·workstealing
点心的游戏开发世界21 小时前
Unity C# 脚本学习笔记:泛型
学习·unity·c#