C#静态与非静态

本文解析了静态特征在软件开发中的作用及其实现原理,探讨了静态与非静态成员的区别,包括静态类、静态构造函数、静态字段等,并分析了它们如何帮助优化内存使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

为什么需要静态特征

在自定义类或看.NET Framework类库中都可以发现,类中大部分都是具体实例特征(也就是没有static标识的),同时我们也能看到一些具有静态特征的类或成员,例如我们经常使用的Console类以及WriteLine方法就是静态的。

然而有些朋友会疑惑,为什么还要有静态特征的呢?干脆都定义为实例的好了?然后静态特征的存在肯定有他存在的原因,并不是我们就是要这么定义的。

技术的实现源于生活,静态特征的存在也是源于生活的,对于类好比就是我们现实生活中的人或事物,静态特征和非静态特征就好比生活中人或事物具有的特征,我们询问人的时候或者电视剧警察查案件的时候,都会听到这样一句话“那个人有什么特征?”或“嫌疑犯有什么特征,多高,年龄等” 其实高度,年龄,性别都是一个人的特征,所以这些在语言范畴就需要为其定义了,也就是我们定义的实例成员了,然而有些特征需要被所有对象实例所共有的,这些特征在语言范畴就定义为静态特征,具体哪些特征可以定义为静态特征呢?

我们在开发过程中必不可少的一个流程就是需求分析了,只有在了解客户需求的条件下才能进行之后的所有流程的,例如一个班级有很多学生,每个学生是一个实体,在语言范畴就可以定义一个类,当我们需要一个学生的时候就可以通过new关键字创建一个出来。然而我们创建出来的学生他们都有一些共同的特征,如同一个班级,学校等,如果我们把班级、学校这样的特征也定义为实例的话。那么我们不是每次创建对象实例的时候都为这些共有的特征分配一次内存的,这样不仅是对内存空间的浪费也是不满足生活常识的,此时我们就可以把班级、学校这样的特征定义为静态特征,这样所有实例都可以共享这两个特征,并且不需要为每个对象实例分配内存。

比较静态特征和非静态特征

静态类与非静态类
  • 静态类和非静态类在C#中定义基本是一样的,只是静态类定义要加上static修饰符而已
  • 静态类只能包含静态成员,否则会出现抛出编译错误;然而非静态类既可以包含非静态成员也可以包含静态成员
  • 静态类是不能实例化,之所以不能实例化,是因为静态类会导致C#将该类同时标记为abstract和sealed,并且编译器不会在类型中生成一个实例的构造函数,从而导致静态类不能实例化,具体原因可以见下图;非静态类可以,并且静态成员的访问只能通过类来进行访问,因为静态成员是属于类的。
public static class StaticClass
    {
        private static string name;
    }

上面代码用IL反汇编程序得到的IL代码结构为:
在这里插入图片描述

静态构造函数与实例构造函数

静态构造函数用来初始化类中的静态成员的,包括静态字段和静态属性,并且静态构造函数是不能带有参数,不能访问修饰符,静态构造函数的调用是由CLR第一次调用类成员之前进行的。

下面还是直接总结下静态构造函数与实例构造函数之间的区别:

  • 静态构造函数可以与无参的实例构造函数同时存在
  • 静态构造函数在CLR加载类时执行,然而实例构造函数在每次实例创建时都会执行
  • 静态构造函数只能对静态成员初始化,不能对非静态成员进行初始化操作,然而实例构造函数,既可以初始化实例成员也可以初始化静态成员,但静态只读字段除外
  • 静态构造函数只被执行一次,但是CLR也不能确定它什么时候被执行,它的执行方式有两种,precise和before-field-init。而实例构造函数在每次创建对象实例都会被执行,创建几个就会执行几次
  • 一个类只能有一个静态构造函数,却可以有多个实例构造函数

静态字段的初始值在静态构造函数之前被指定,构造函数的执行顺序大致如下图所示:
在这里插入图片描述

静态字段、属性和实例字段、属性

下面就直接总结下它们之间的区别:

  • 静态成员包括静态属性和静态字段,静态字段一般实现为private,静态属性一般实现为public,从而来体现类的封装性
  • 静态成员和类相关联,不依赖于对象而存在,只能由类来访问;实例成员与具体类相关联,只能由对象实例访问
  • 静态成员不管创建多少实例对象,都在内存中只有一份,实例成员每创建一个实例对象,都会在内存中分配一块内存区域。
静态方法与实例方法

类似于静态字段和属性,静态方法共享代码段,同样以static关键字来标识静态方法,对于他们之间的区别总结为:

  • 静态方法只能访问静态成员和方法,但是可以间接通过创建实例对象来访问实例字段、属性和方法;实例方法既可以访问实例成员也可以访问静态成员
  • 静态方法由类方法,实例方法由对象访问
  • 静态方法不能引用this关键字,而实例方法可以
  • 静态方法不能被标识为virtual、abstract或override,静态方法可以被派生访问,但是不能被派生类重写
  • Main方法为静态的,所以Main方法不能直接访问类中的实例字段、属性和方法,否则编译器会报错
  • 静态方法一般用于作为通知的工具来实现
  • 在性能上,静态方法和实例方法的差别不大。因为,它们都是在JIT加载类的时候分配内存的,不同的是静态方法是以类为引用,而实例方法是以对象为引用,创建实例时,不会再为静态方法分配内存,所有实例对象共用一个类的方法代码,所以,静态方法和实例方法的调用,区别仅在于静态方法可以直接调用,而实例方法需要当前对象指针指向该方法,在性能上差别并不大。
### C#静态方法与实例方法的区别 在 C# 编程语言中,静态方法和实例方法之间存在显著差异。这些差异不仅影响编码风格的选择,还决定了程序运行时的行为特性。 #### 定义与声明方式 - **静态方法**:由 `static` 关键字修饰的方法称为静态方法。这类方法属于类本身而非特定的对象实例。因此,可以通过类名直接调用而不必创建任何对象[^2]。 - **实例方法**:未加 `static` 修饰符的方法即为实例方法。只有当创建了一个具体的对象之后才能对该对象上调用此类方法。 ```csharp public class ExampleClass { public static void StaticMethod() { /* ... */ } // 静态方法 public void InstanceMethod() { /* ... */ } // 实例方法 } ``` #### 访问范围 - **静态方法**无法访问非静态成员(字段、属性或其他方法),除非通过传递参数的形式间接获取某个具体对象的信息。 - **实例方法**可以直接访问同一类内的所有成员——无论是静态还是非静态的成员都可以自由存取。 #### 内存管理 - **静态方法**在整个应用程序生命周期内仅初始化一次,并驻留在全局堆栈区直到应用终止。这意味着即使多次调用了同一个静态函数也不会重复分配额外的空间给它自己[^3]。 - **实例方法**每次新建一个对象都会为其分配独立的工作区域用于存储局部变量等临时数据结构。随着对象销毁,这部分内存也会随之回收释放回系统池中等待再利用。 #### 并发处理能力 考虑到多线程环境下的安全性: - 对于那些可能被多个线程同时执行的任务而言,应当谨慎对待共享资源的竞争条件问题。由于静态上下文中所有的状态都是共有的,所以在高并发场景下容易引发竞态错误或死锁现象。此时建议优先选用实例化版本以确保各自拥有独立的状态副本从而避免冲突发生[^4]。 - 当确认所涉及的操作不具备上述风险因素时,则可以选择效率更高的静态实现形式来简化代码逻辑并提升整体性能表现。 #### 应用场景举例说明 基于以上几点分析可知: - 如果某功能模块完全依赖外部输入参数完成计算过程而无需借助内部维护的数据项的话,那么将其定义成静态形式是比较合理的做法之一[^1]。 - 反之对于那些需要频繁读写私有域值或是与其他关联实体交互协作的情况来说,显然更加适合构建成为常规意义上的成员函数以便充分利用面向对象机制所带来的诸多便利之处。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值