除了创建帮助器之外,创建静态方法的理想情况(实际示例)是什么?

我只是想了解静态方法所服务的目的以及我可以创建静态方法的理想情况,除了有些人会说静态方法用于创建帮助器。

考虑我有一个网站,将在我的公司使用,就像人力资源管理系统,如网站。

现在,管理员登录系统管理员后,将看到员工列表。因此,该方法很简单,除了从员工表中获取员工的所有详细信息,并将在网站上显示这些方法,此方法将在业务中定义在.net中访问这样的层:

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

这就是我如何从我的应用程序中调用此方法。对于Eg(.aspx页面或mvc控制器等….)

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

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

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

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

所以我的function与上面在Business acess层中定义的相同:

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

所以我的问题与是否将此方法创建为静态方法或非静态方法相同,如果同时有超过10个用户同时访问此网站会发生什么,那么这种方法的行为/含义是什么?

如果我们将此方法声明为静态,这个方法是否会满足每个用户的目的?

有人可以简单地解释每个场景来回答这个问题吗???

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

如果没有,那么为什么对象ab存在? 拥有对象的重点是将一些不同的状态与它们联系起来。 如果两个不同的对象永远不能引用不同的状态,那么它们就没有任何意义。

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

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

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


1这两个根本不同的概念( 模块 )之间缺乏区别实际上非常烦人,而C#这种行为的主要原因是因为它被认为与Java类似。 事后看来这是一个错误,但不是一个严重的错误。

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

另一方面,如果A类依赖于通过其构造函数提供的IEmployeeProvider类的接口 ,那么您可以使用IEmployeeProvider实现来测试A类。

如果A在其构造函数IEmployeeProvider作为参数,那么您可以通过查看构造函数来判断它依赖于IEmployeeProvider 。 但是,如果它依赖于方法内某处的静态EmployeeProvider类,则隐藏依赖项。 你必须阅读全class以了解它的依赖性。

此外,静态类本身可能更难测试。 除非它绝对总是保持无状态,否则最好有一个可以unit testing的非静态类。

只要方法不访问诸如字段或属性之类的静态状态,就可以让多个线程执行相同的静态方法。 在这种情况下,存储在fields / properties中的共享对象本身必须是线程安全的。 .Net的数据访问部分不是为了线程安全而设计的。

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

然而,这是一个非常大的主题,一般来说,类依赖的管理在OOP中是非常棘手的。 一些程序员喜欢将此任务委托给“控制反转” – 图书馆。 有许多可用于.Net,如Microsoft Unity,StructureMap,AutoFac等。

如果我们要讨论静态,我们需要引入依赖。 在这种情况下,它是一个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"); } } 

通过接口的dependency injection会改变一切。 现在这个方法很好,因为它只处理接口和字符串。 这两者都是无国籍的。 由于该方法的所有组件都是无状态的,因此对于只能有一个全局状态的静态方法来说它们是完全安全的。

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

一个更好的例子就像sql客户端的工厂。 例如,使用nhibernate时,应该只创建一个会话工厂。 一个线程安全会话工厂可以创建多个非线程安全会话来运行SQL查询。 在这种情况下,通过静态方法公开会话工厂是合适的,因为它描述了只有一个会话工厂的事实。

 var session = SessionFactory.OpenSession(); 

回答你的问题:

所以我的问题是我应该将此方法创建为静态方法还是非静态方法? 注意:这只是方法的一个示例,此方法位于我的业务访问层中。

我会将这些方法设为静态 – 给出你提供的内容。 但我敢打赌你会在你的类中或在该类中的方法中声明实例变量,这当然意味着不要使它变为静态。


因此,如果我决定使用静态方法,对我来说决定因素与重用和资源有关。

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

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

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

我对编程也很陌生,我希望你发现这很有帮助。

使用静态方法相当于具有全局行为。 它带来了诸多好处:简单方案的易用性。

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

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

Java有这个,但scala修改了这个。 更多相关内容: 为什么Scala在类中没有静态成员?

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

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

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

现在,回答你问题的要点(我知道,最后):

  • 我认为业务访问层不应该是静态的,因为您很可能需要非静态类的好处,例如dependency injection和单元可测试性。
  • 从线程/multithreading的角度来看,静态和非静态方法之间没有区别,它们可以同时由多个线程调用,并且所有这些都将同时执行(除非使用同步构造)。 但是,有一个共同的设计建议,如果您期望竞争条件,您应该使静态方法成为线程安全的。 非静态方法不必担心这一点,因为这会使它们陷入太多假设。