关于静态班级和成员的思想和最佳实践


11

我对有关静态成员或整个静态类的想法和行业最佳实践感到很好奇。这有什么弊端,或者它参与任何反模式?

我将这些实体视为“实用程序类/成员”,从某种意义上说,它们不需要或不需要实例化该类来提供功能。

关于此的一般想法和行业最佳实践是什么?

请参阅下面的示例类和成员,以说明我所引用的内容。

// non-static class with static members
//
public class Class1
{
    // ... other non-static members ...

    public static string GetSomeString()
    {
        // do something
    }
}

// static class
//
public static class Class2
{
    // ... other static members ...

    public static string GetSomeString()
    {
        // do something
    }
}

先感谢您!


1
对静态类和静态方法MSDN文章提供了一个相当不错的待遇。
罗伯特·哈维

1
@Robert Harvey:尽管您引用的文章很有用,但是它在最佳实践方面以及使用静态类时的一些陷阱方面并没有提供太多帮助。
伯纳德

Answers:


18

通常,避免使用静电-尤其是任何静态。

为什么?

  1. 静电会导致并发性问题。由于只有一个实例,因此它在并发执行中自然共享。这些共享资源并发编程的克星,而且往往不做需要被共享。

  2. 静电会给单元测试带来麻烦。任何值得一试的单元测试框架都可以同时运行测试。然后您遇到#1。更糟糕的是,您使测试与所有设置/拆解工作以及将要尝试共享“设置”代码的黑客混为一谈。

也有很多小东西。静力学趋于僵化。您无法接口它们,无法覆盖它们,无法控制它们的构建时间,不能在泛型中很好地使用它们。您无法真正对其进行版本控制。

当然有静态的用法:常量值的作用很大。不适合单个类的纯方法在这里可以很好地工作。

但总的来说,避免使用它们。


我对您所无法实现的并发程度感到好奇。您可以对此进行扩展吗?如果您的意思是可以无隔离地访问成员,那将是很合理的。您通常使用的静态用例是:常量值和纯/实用方法。如果那是最好的方法,并且99%的静态用例,那么这给了我一定程度的舒适感。另外,您的答案+1。很好的信息。
Thomas Stringer 2014年

@ThomasStringer-线程/任务之间的更多接触点意味着更多的并发问题机会和/或同步过程中更多的性能损失。
Telastyn 2014年

因此,如果一个线程当前正在访问静态成员,那么其他线程需要等待直到拥有线程释放资源?
Thomas Stringer 2014年

@ThomasStringer-也许,也许不是。在这方面,静态(几乎在大多数语言中)与任何其他共享资源没有什么不同。
Telastyn 2014年

@ThomasStringer:不幸的是,实际上这比这还糟,除非您标记该成员volatile。如果没有易失性数据,您将无法从内存模型中获得任何保证,因此在一个线程中更改变量可能不会立即或根本不会反映出来。
Phoshi 2014年

17

如果功能是“纯”的,我认为没有问题。纯函数仅在输入参数中起作用,并基于此提供结果。它不依赖于任何全局状态或外部上下文。

如果我看您自己的代码示例:

public class Class1
{
    public static string GetSomeString()
    {
        // do something
    }
}

此函数不带任何参数。因此,它可能不是纯粹的(此函数的唯一纯粹实现将是返回一个常量)。我假设该示例不能代表您的实际问题,我只是指出这可能不是一个纯函数。

让我们举一个不同的例子:

public static bool IsOdd(int number) { return (number % 2) == 1; }

这个函数是静态的没有什么错。我们甚至可以将其作为扩展功能,以使客户端代码更具可读性。扩展功能基本上只是一种特殊的静态函数。

Telastyn正确地提到并发是静态成员的潜在问题。但是,由于此函数不使用共享状态,因此这里没有并发问题。一千个线程可以同时调用此函数,而没有任何并发​​问题。

在.NET框架中,扩展方法已经存在了一段时间。LINQ包含许多扩展功能(例如Enumerable.Where()Enumerable.First()Enumerable.Single()等)。我们不认为这些不好,是吗?

当代码使用可替换的抽象时,单元测试通常可以从中受益,从而允许单元测试将测试代码替换为系统代码。静态函数禁止这种灵活性,但这在体系结构层边界非常重要,在这种情况下,我们想用伪造的数据访问层代替实际的数据访问层。

但是,当为一个行为不同的对象编写测试时,根据某个数字是奇数还是偶数,我们实际上并不需要能够IsOdd()用替代实现替换该函数。同样,我看不到何时需要Enumerable.Where()出于测试目的提供其他实现。

因此,让我们检查一下此功能的客户端代码的可读性:

选项a(将函数声明为扩展方法):

public void Execute(int number) {
    if (number.IsOdd())
        // Do something
}

选项b:

public void Execute(int number) {
    var helper = new NumberHelper();
    if (helper.IsOdd(number))
        // Do something
}

静态(扩展)功能使第一段代码更具可读性,并且可读性很重要,因此请在适当的地方使用静态功能。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.