除了创建助手之外,创建静态方法的理想情况(现实生活中的例子)是什么?

What are the desirable situation (real life example) to create static methods except for creating helper?

我只想了解静态方法的用途以及我可以创建静态方法的理想情况是什么,除了有人会说静态方法用于创建助手。

假设我有 1 个网站,仅供我公司使用,例如 人力资源管理系统 等网站。

现在管理员登录系统后,管理员会看到employees.so的列表,方法很简单,无非就是从员工table获取员工的所有详细信息,并将它们显示在网站和此方法将在 .net 中像这样在业务访问层中定义:

 public class EmployeeBal
    {
        public List<Employee> GetAllEmployees()
        {
                 return Select * from Employee
        }
     }

这就是我从 application.For 中调用此方法的方式,例如(.aspx 页面或 mvc 控制器等....)

var employeeBal= new EmployeeBal();
employeeBal.GetAllEmployees();

所以我的问题是我应该将此方法创建为静态方法还是非静态方法?

注意:这只是一个方法示例,这个方法在我的业务访问层

假设我有 1 个电子商务网站,我在主页上显示了一些产品列表,访问该网站时每个用户都可以看到该产品列表。

所以我的功能将与上面在业务访问层中定义的相同:

public class ProductBal
    {
        public List<Product> DisplayProductonHomePage()
        {
                 return Select * from Products
        }
     }

所以我的问题是将此方法创建为静态方法还是非静态方法,如果超过 10 个用户同时访问该网站会发生什么,那么 behaviour/implications这个方法???

如果我们将此方法声明为静态的,此方法是否会为每个用户服务?

任何人都可以通过简要解释每个场景来回答这个问题吗???

可以让多个线程执行同一个静态方法,只要该方法不访问字段或属性等静态状态即可。在那种情况下,存储在 fields/properties 中的共享对象本身必须是线程安全的。 .Net 的数据访问部分未设计为线程安全的。

一旦您开始考虑管理可在执行单个 Web 请求期间重复用于多个查询的数据库连接等方面,您就应该考虑静态是否是最佳方法。由于您不能如上所述将连接存储在静态字段中,因此您必须将其作为参数传递给每个静态方法。另一方面,如果将连接传递给构造函数并将其存储在(非静态)字段中,则可以从该实例的多个非静态方法访问它,这将使 IMO 更易于管理。

然而,这是一个很大的话题,一般来说,class 依赖关系的管理在 OOP 中是相当棘手的。一些程序员更喜欢将此任务委托给 "Inversion of Control"-library。 .Net 可用的有很多,例如 Microsoft Unity、StructureMap、AutoFac 等

当没有状态需要维护时,静态方法才有意义。我所说的状态是什么意思?那么,请考虑以下情况:您有两个不同的对象,ab,它们都是 EmployeeBal 类型。在您的程序中是否曾经 a.GetAllEmployees()b.GetAllEmployees() 会产生不同的结果?

如果不存在,那么为什么对象 ab 存在呢?拥有对象的全部意义在于将一些不同的状态与它们相关联。如果两个不同的对象永远不能引用不同的状态,那么它们就没有任何意义。

事实上,在这种情况下,您的 EmployeeBal 将完全等同于 System.Math,并且它的所有方法都是“辅助方法”(如果您希望这样称呼它们的话)。在这种情况下,暂时忘掉静态方法:你的整个 class 应该是静态的(static class EmployeeBal),并且它不应该有任何构造函数;因为 EmployeeBal 类型的对象的概念根本没有意义。事实上,在其他语言中 EmployeeBal 根本不是 class;相反,它通常被称为 模块 :一个逻辑上对代码进行分组的单元。 C# 没有模块,所有代码必须位于 classes 内。 类 因此实现了双重目的:它们对代码进行分组,并生成对象。1

现在考虑一个不太极端的情况:EmployeeBal 对象实际上保持状态,并且不同。然而 GetAllEmployees() 仍然会产生相同的结果,无论哪个对象调用该方法。

在这种情况下,EmployeeBal显然不能是静态的class。但是 GetAllEmployees 仍然是无状态的,因此不属于类型 EmployeeBalobjects。因此该方法应该是静态的。


1 这两个根本不同的概念(模块class) 实际上很烦人,C# 这样做的主要原因是因为它被认为类似于 Java。事后看来这是一个错误,但并不严重。

方法应该是静态的有什么原因吗?如果不是,我总是支持非静态。 一个重要原因是能够编写单元测试。 为了编写单元测试,您希望能够将您正在测试的 class 与其他 class 隔离开来。但是,如果 class A 包含对静态 class B 的引用,那么您无法在不测试 B 的情况下测试 A。也许 B 取决于连接字符串或配置设置。也许 B 依赖于其他静态 classes。现在你无法测试 A,除非 B 和 所依赖的一切都已到位。

另一方面,如果 class A 依赖于通过其构造函数提供的 接口 ,例如 IEmployeeProvider,那么您可以测试 class A 具有 IEmployeeProvider.

的模拟实现

如果 A 在其构造函数中将 IEmployeeProvider 作为参数,那么您可以通过查看构造函数来判断它依赖于 IEmployeeProvider。但是,如果它依赖于方法内部某处的静态 EmployeeProvider class ,则依赖项将被隐藏。你必须阅读整个 class 才能知道它取决于什么。

此外,静态 class 本身可能更难测试。除非它绝对总是保持无状态,否则最好有一个非静态 class 可以进行单元测试。

回答你的问题:

所以我的问题是我应该将此方法创建为静态方法还是非静态方法? Note:This只是一个方法的例子,这个方法在我的业务接入层

鉴于您提供的内容,我会将这些方法设为静态。但我敢打赌,您会在 class 中声明实例变量,或者在 class 中的方法中声明实例变量,这当然意味着不要将其设为静态。


所以对我来说决定是否使用静态方法的一个决定因素与重用和资源有关。

如果我发现自己多次重复使用某个方法,并且我认为它不需要状态(保存在内存中)- 我会将其设为静态方法。

此外,如果我的方法可以在其他应用程序中使用,或者如果我认为它们将来会有用,我通常会将它们设为静态。

例如,我最近编写了一个将 excel 文件转换为平面文件的方法。我在它自己的 static class 中使它成为一个静态方法(我可能会把它放在类似的实用程序 class 中)因为我可能最终会在另一个项目中再次使用它,所以我现在可以只需引用它的 class 而不必实例化一个新对象来调用该方法。 (反正我不需要状态)

我对编程也很陌生,希望对您有所帮助。

静态和非静态方法的用例不同,因此您需要根据它们满足的需求创建一个:

  • 静态方法不参与基于继承的多态性,而非静态方法参与。换句话说,你不能将静态方法标记为虚拟或抽象,这意味着你不能改变它的行为。这也意味着静态方法的调用者确切地知道什么这个静态方法将要做什么以及如何。使用非静态方法,您可以在基础 class 上调用它,但由于多态性,您最终可能会调用具有覆盖行为的派生 class 方法。
  • 静态和非静态方法都可以改变某种状态(与其他人声称的相反),但还是有区别的。您可以设计一个包含所有静态成员(属性、方法等)的静态 class,因此这些方法可以改变此静态 class 的状态(也就是说,即使 C# 允许你这样做,我不建议创建这样的 class 无论如何)。使用非静态方法,您可以同时更改 class 的静态和非静态状态。这进一步说明了静态和非静态 classes 之间的区别,简而言之,静态 class 是一个 具体 实例,而非静态class 可以相乘并且它们每个都有自己的状态副本(那么为什么要设计一个带有人为限制的静态 class - 这就是我之前不推荐它们的原因)。
  • 静态方法的另一个很好的用法是扩展方法。这些应该定义为静态的,但您可以在它们扩展的 class 的实例上调用它们。它们仍然作为实例的 外部 快捷方式,因为它们只能做常规静态方法(例如不能访问私有或受保护成员)。
  • 你是对的,static class 非常适合定义辅助方法,因为它们通常只是一些固定功能的快捷方式,可以很容易地从许多地方重新执行它。在 Visual Basic 中,您可以使用 shared 关键字代替 static 关键字,这很好地解释了静态方法的用途。

最后,我个人建议创建静态方法 Pure functions,它总是为相同的输入产生相同的输出(没有副作用,例如输出因时间或其他隐含因素而不同)。您应该有充分的理由以其他方式设计它(例如,如果您正在编写 Math.Random())。

现在,回答你问题的要点(我终于知道了):

  • 我认为业务访问层不应该是静态的,因为您很可能需要非静态 classes 的好处,例如依赖注入和单元可测试性。
  • 从threading/multithreading的角度来看,静态方法和非静态方法没有区别,它们都可以被多个线程同时调用,并且它们都将同时执行(除非使用同步结构).但是,有一个常见的设计建议,如果您预计会出现竞争条件,则应该使静态方法线程安全。非静态方法不必担心这一点,因为这会使它们陷入太多假设。

使用静态方法等同于具有全局行为。它带来的好处是:简单场景的轻松访问。

它还伴随着全局数据和状态的所有问题。其中,您不能用一个实现替换另一个实现(例如测试)。参见 https://softwareengineering.stackexchange.com/questions/148108/why-is-global-state-so-evil

虽然您可能认为您没有全局状态……但从概念上讲您有。您有一种独特的、预定的、不可配置的、硬编码的方式来访问某些行为。你发布了它,你不能改变它......永远。你打破了开闭原则。你违反了liskov替换原则。

Java 有这个但是 scala 修改了那个。更多相关信息:Why doesn't Scala have static members inside a class?

如果要讲静态的话,就需要引入依赖。在本例中,它是一个 sql 客户端。这是引入的代码的样子。由于我们不打算深入了解 sql 客户端的细节,因此它在静态方法中用作接口。

var client = new SqlClient();
var allEmployeeData = EmployeeBal.GetAllEmployees(client);

class EmployeeBal
{
    public static Employee GetAllEmployees(ISqlClient client)
    {
        return client.Execute("Select * from Employee");
    }
}

通过接口的依赖注入改变了一切。现在这个方法是静态的,因为它只处理一个接口和一个字符串。这两个都是无状态的。由于该方法的所有组件都是无状态的,因此它们对于只能具有一个全局状态的静态方法来说是完全安全的。

由于您的代码是最初编写的,所以它作为静态代码并不安全,因为我怎么能确定 sql 客户端已准备好使用,而且在我检查它已准备就绪之后还没有当我去 运行 查询时改变了?如果我可以注入 sql 客户端,我就可以管理它,因为它具有本地和全局范围。

一个更好的例子是 sql 客户的工厂。例如,对于 nhibernate,应该只创建一个会话工厂。一个线程安全会话工厂可以为 运行ning sql 查询创建多个非线程安全会话。在这种情况下,通过静态方法公开会话工厂是合适的,因为这描述了这样一个事实,即永远只会有一个会话工厂。

var session =  SessionFactory.OpenSession();