通过Static 字段来维护状态是不是一个好主意

Posted badlands

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通过Static 字段来维护状态是不是一个好主意相关的知识,希望对你有一定的参考价值。

Static是申明静态字段、静态方法或者静态类的修饰符。使用Static申明的字段属于类型本身而不属于任何字段,声明的类也具有一些特别特性,比如不能实例化,不能继承等。用通俗化的语言来说,static字段即使创建多个类型实例也只会声明一次,应为他属于类型。它在所有类实例之间皆可访问。可以认为静态字段是类型的全局变量。

我会在一些场景下使用static字段,出现次数最多的就是作为函数/方法修饰符或者简单的单例模式。我个人认为,static函数类似于C函数(method),而一般中function称呼实例方法。静态函数与实例无关,更多的体现出一种无关联性。在字段(property)上,就很少使用static修饰符了。除非内部实例需要临时计数,或者具有无状态的其他类型的实例对象字段。

最近考虑到设计一个串口通信的装饰类(基于SerialPort, System.IO.Port),用来扩充SerialPort的功能,比如帧完整性检查、超时检测与重传等。在我开始工作之前,已经有了一些基本实现。先不关注装饰器的功能,该类通过简单工厂函数来创建实例,并在内部维护了基于名字的实例列表。在每次打开一个串口后,会在列表中以<Key,Value>的形式登记一下,在内存中维护了一个打开过串口的设备列表。

    public class SerialPortDecorator : IDisposable
    {
        static public Dictionary<string, SerialConn> _PortDict = new Dictionary<string, SerialConn>();
        private SerialPort _SerialPort = null;

/// <summary> /// Class for Serial Connection to a Comm port. /// </summary> /// <remarks> /// This class should be thread safe and non blocking. It has two modes, one where /// a reply is expected (will callback a delegate with response) and one where no /// reply is expected. More complex handling of reply and expected response is handled /// by the caller. /// </remarks> /// <param name="portNameIn"></param> /// <returns></returns> public static SerialConn GetSerialConn(string portNameIn) { lock (_StaticLockObj) { // TODO validate the portname if (!_PortDict.ContainsKey(portNameIn)) { SerialConn sc = new SerialConn(); sc.Initialize(portNameIn); _PortDict.Add(portNameIn, sc); } return _PortDict[portNameIn]; } }

通过静态字段来维护这样一个状态,确实显得很有技巧。因为我从来不这么做,所以我觉得有点惊讶。当然,这并不代表这种实现方式不好。我的疑问有如下几点:

  1. 已打开设备列表并不属于装饰类的功能/概念,设备列表属于整个App或串口管理中心或者作其他逻辑概念。
  2. 只能提供打开过或者注册过的串口列表,而不能提供未打开的串口列表,即使这些串口列表是存在的。如果需要查看枚举串口,显然做不到。
  3. 如果外部需要知道当前的设备列表,通过SerialPortDecorator._PortDict来达到目的实在太奇怪。

在MSDN C#编程指南中,有一段static的示例代码:

/***为了说明静态成员,请看一个表示公司雇员的类。 假设该类包含一种对雇员计数的方法和一个存储雇员数的字段。 该方法和字段都不属于任何实例雇员, 而是属于公司类。 因此,应该将它们声明为此类的静态成员。
示例
此示例读取新雇员的姓名和 ID,将雇员计数器加一,并显示新雇员的信息和新的雇员数。 为简单起见,该程序从键盘读取当前的雇员数。 在实际的应用中,应从文件读取此信息。
C#
***/
public class Employee4
{
    public string id;
    public string name;

    public Employee4()
    {
    }

    public Employee4(string name, string id)
    {
        this.name = name;
        this.id = id;
    }

    public static int employeeCounter;

    public static int AddEmployee()
    {
        return ++employeeCounter;
    }
}

“该方法和字段都不属于任何实例雇员, 而是属于公司类。”这句话就指出了static字段的语义。基于此,对于疑问1不置可否。

关于疑问2,枚举未连接串口列表很明显属于不同的语义。使用static字段无法提供这些内容,这些内容也不适合放在装饰类中。提出来,只是为了说明需求环境。如果真的有如此需求,static字段很明显不适合。在一定程度上指出了static的应用场景——语义单一、明确。

疑问3就很重要了。在上面的MSDN示例中,静态字段employeeCounter只是为了生成增序ID。虽然属于公司,但只为员工编号服务。现实应用中并不会通过查询该ID来查询员工数量,一般只会用在批量环境或者查询结果中生成序列ID。从此示例来看,我觉得static 字段的应用范围应该限制在包含它的类上。

对于疑问1,2,3的讨论,我想说static字段的应用场景和能力具有语义限制。static字段在语义上不属于当前类,属于当前名词的匿名拥有者。在使用上,最好只在其拥有类中进行使用,不用超过类范围。

这只是我个人的看法。

以上是关于通过Static 字段来维护状态是不是一个好主意的主要内容,如果未能解决你的问题,请参考以下文章

使用 base64 编码文本而不是 multipart/form-data 上传文件 - 好主意吗?

通过唯一的成员 ID 来识别对象是个好主意吗?

为啥在高流量网站中使用 Session 存储状态是个坏主意?

将一个更大的数据库表分成更多表是个好主意吗

在一个flutter项目中使用两种状态管理是个好主意吗

命名块来限制变量范围:好主意吗?