在静态类中使用状态是否是不好的实践?

12

我想要做类似这样的事情:

public class Foo {
    // Probably really a Guid, but I'm using a string here for simplicity's sake.
    string Id { get; set; }

    int Data { get; set; }

    public Foo (int data) {
        ...
    }

    ...
}

public static class FooManager {
    Dictionary<string, Foo> foos = new Dictionary<string, Foo> ();

    public static Foo Get (string id) {
        return foos [id];
    }

    public static Foo Add (int data) {
        Foo foo = new Foo (data);
        foos.Add (foo.Id, foo);

        return foo;
    }

    public static bool Remove (string id) {
        return foos.Remove (id);
    }

    ...

    // Other members, perhaps events for when Foos are added or removed, etc.
}

这样可以让我从任何地方管理全局的Foo集合。然而,我被告知静态类应该始终是无状态的——不应该使用它们来存储全局数据。总体而言,全局数据似乎备受诟病。如果我不应该使用静态类,那么正确的方法是什么?

注意:我找到了一个类似的问题,但给出的答案并不适用于我的情况。

7个回答

11

谁说静态类应该是无状态的?"Static" 意味着 "静态的"。

了解一下在 CLR 中静态类的工作方式:

  • 您无法控制静态构造函数被调用的时间。
  • 对于每个调用程序,静态类都有一个单独的状态。

此外,请注意并发问题。

顺便说一句,我很惊讶人们经常说“不要使用 X”,这就像有人走进你的工具棚,指着半打工具说:“这些工具是不好的实践。” 这没有意义。


1
使用它时要明智,避免问题。随后灵活转移到更好的东西。 - Lex Li

4
全局数据既强大又常见问题,这就是为什么使用依赖注入等技术。您可以将其视为普通的解耦问题。在程序中直接引用了许多地方的全局数据会导致该全局数据与所有这些位置之间的强耦合。
然而,在您的示例中,您已将对数据的访问隔离到一个类中,并控制了全局数据的访问细节。由于某些全局数据通常是不可避免的,我认为这是一种好方法。
例如,您可以比较一下.NET框架如何通过静态类System.Configuration.ConfigurationManager和静态属性AppSettings访问app.config和web.config。它们隐藏了如何访问全局数据的详细信息。

3

这并不是一般的坏事。在某些罕见情况下,这比实现其他带有大量开销的方法更为必要。

但建议注意线程安全。

您应该锁定对字典的每个调用,以便一次只有一个线程可以访问它。


private static readonly object LockStaticFields = new object();

public static Foo Add (int data) {
        lock(LockStaticFields)
        {
           Foo foo = new Foo (data);
           foos.Add (foo.Id, foo);

           return foo;
        }
    }


如果你在多线程环境下工作,我建议你添加:“你应该锁定每次对字典的调用”。否则,这将是无用的开销。 - Budda
嗯,也许你是对的。但我的经验告诉我最好总是按照这种方式做。我现在在一家公司工作,他们现在必须重构几乎所有代码,因为最初它没有为多线程环境实现。我认为这比第一次实现线程安全可能不需要这种安全性要花费更多。 - DHN

3
你似乎在寻找的是单例类,而不是静态类。静态类和方法应该用于无状态例程。单例类在应用程序运行时只实例化一次,并具有完整的类功能。将来每次引用它时,您都将获得完全相同的实例和成员属性。
第一个谷歌搜索结果"C# singleton"似乎有一个相当不错的实现解释。http://www.yoda.arachsys.com/csharp/singleton.html

1
需要注意的是,单例模式实践起来并不好,因为它会创建依赖关系等问题。最终,控制反转(IoC)和依赖注入会是更好的解决方案。 - Femaref

0
在你的类中使用只读静态属性,这个属性将在类的所有实例中保持一致。从构造函数等处递增和递减它。

0

我经常在静态类中使用列表来保存永远不会(或极其罕见)更改的内容 - 这对于加载选项列表等内容而无需每次都访问数据库非常方便。由于我不允许更改,所以不必担心锁定/访问控制。


0

还有一件需要考虑的事情是应用程序本身和其预算。是否真的需要比静态类更复杂的东西?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接